Skip to content

Instantly share code, notes, and snippets.

@aloisdeniel
Last active October 2, 2023 22:00
Show Gist options
  • Save aloisdeniel/9d584ba79cbf5454009b139f21926ae6 to your computer and use it in GitHub Desktop.
Save aloisdeniel/9d584ba79cbf5454009b139f21926ae6 to your computer and use it in GitHub Desktop.
Flutter custom layout
import 'dart:math' as math;
import 'package:flutter/material.dart';
const darkBlue = Color.fromARGB(255, 18, 32, 47);
const barBackground = Color(0xFFE9EAF6);
const barForeground = Color(0xFF0B0B10);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Container(
width: 420,
height: 48,
color: barBackground,
child: const TitleBar(
title: Text(
'Title',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20,
color: barForeground,
),
),
leading: TitleAction(Icons.menu),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
TitleAction(Icons.share),
TitleAction(Icons.search),
TitleAction(Icons.notifications_none),
],
),
),
),
),
),
);
}
}
enum _LayoutId {
leading,
title,
trailing,
}
class TitleBar extends StatelessWidget {
const TitleBar({
super.key,
required this.title,
required this.leading,
required this.trailing,
});
final Widget title;
final Widget leading;
final Widget trailing;
@override
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: _LayoutDelegate(),
children: [
LayoutId(
id: _LayoutId.trailing,
child: trailing,
),
LayoutId(
id: _LayoutId.title,
child: title,
),
LayoutId(
id: _LayoutId.leading,
child: leading,
),
],
);
}
}
class _LayoutDelegate extends MultiChildLayoutDelegate {
_LayoutDelegate();
@override
void performLayout(Size size) {
final leadingSize = layoutChild(
_LayoutId.leading,
BoxConstraints(
maxHeight: size.height,
maxWidth: size.width,
),
);
final trailingSize = layoutChild(
_LayoutId.trailing,
BoxConstraints(
maxHeight: size.height,
maxWidth: size.width - leadingSize.width,
),
);
final titleSize = layoutChild(
_LayoutId.title,
BoxConstraints(
maxHeight: size.height,
maxWidth: size.width - leadingSize.width - trailingSize.width,
),
);
final center = size.center(Offset.zero);
positionChild(
_LayoutId.leading,
Offset(
0,
center.dy - leadingSize.height / 2,
),
);
positionChild(
_LayoutId.trailing,
Offset(
size.width - trailingSize.width,
center.dy - trailingSize.height / 2,
),
);
final leadingOverlapping = leadingSize.width // leading end
-
(center.dx - titleSize.width / 2); // title start
final trailingOverlapping =
(size.width - trailingSize.width) // trailing start
-
(center.dx + titleSize.width / 2); // title end
positionChild(
_LayoutId.title,
Offset(
math.max(0, leadingOverlapping) +
math.min(0, trailingOverlapping) +
center.dx -
titleSize.width / 2,
center.dy - titleSize.height / 2,
),
);
}
@override
bool shouldRelayout(covariant _LayoutDelegate oldDelegate) {
return false;
}
}
class TitleAction extends StatelessWidget {
const TitleAction(
this.icon, {
super.key,
});
final IconData icon;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 42,
height: 48,
child: Center(
child: Icon(
icon,
color: barForeground,
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment