CustomMultiChildLayout - wurzelsand/flutter-memos GitHub Wiki
CustomMultiChildLayout
soll aus zwei Children bestehen, aus einem roten und einem grünen Rechteck. Das Delegate soll sich um das Layout kümmern: Die beiden Rechtecke sollen übereinander stehen, wenn der vorhandene Platz für die Rechtecke höher als breit ist. Ansonsten sollen sie nebeneinander stehen.
import 'package:flutter/material.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(
home: Scaffold(
body: ExampleWidget(),
),
);
}
class ExampleWidget extends StatelessWidget {
const ExampleWidget({super.key});
@override
Widget build(BuildContext context) => CustomMultiChildLayout(
delegate: _TwoChildrenLayoutDelegate(ids: [1, 2]),
children: [
LayoutId(
id: 1,
child: Container(color: Colors.red, ),
),
LayoutId(
id: 2,
child: Container(color: Colors.green, ),
),
],
);
}
class _TwoChildrenLayoutDelegate extends MultiChildLayoutDelegate {
_TwoChildrenLayoutDelegate({required this.ids}) {
assert(ids.length == 2);
}
final List<int> ids;
@override
void performLayout(Size size) {
final useVerticalLayout = size.height > size.width;
for (final id in ids) {
final childSize = useVerticalLayout
? layoutChild(id,
BoxConstraints(maxWidth: size.width, maxHeight: size.height / 2))
: layoutChild(id,
BoxConstraints(maxWidth: size.width / 2, maxHeight: size.height));
Offset childPosition = Offset.zero;
for (final id in ids) {
positionChild(id, childPosition);
if (useVerticalLayout) {
childPosition += Offset(0, childSize.height);
} else {
childPosition += Offset(childSize.width, 0);
}
}
}
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false; // #1
}
- Damit ein
performLayout
auch dann ausgeführt wird, wenn sich das Delegate ändert. Hier würde man die Felder des neuen und alten Delegates vergleichen. Wenn sich dabei ein Unterschied ergibt, könnte ich hiertrue
zurück geben.
- Die entscheidenen Methoden des
MultiChildLayoutDelegate
sindlayoutChild
(für die Größe) undpositionChild
(für die Position).
Ich probiere, ob das gleiche auch mit Flow
geht. Dabei berechne ich die Größe der Children bereits im ExampleWidget
und das Delegate kümmert sich nur um die Positionierung. Ich glaube, dass der Einsatzbereich von Flow
eher in Animationen liegt.
import 'package:flutter/material.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(
home: Scaffold(
body: ExampleWidget(),
),
);
}
class ExampleWidget extends StatelessWidget {
const ExampleWidget({super.key});
@override
Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size;
final childSize = screenSize.height > screenSize.width
? Size(screenSize.width, screenSize.height / 2)
: Size(screenSize.width / 2, screenSize.height);
return Flow(
delegate: const _TwoChildrenLayoutDelegate(),
children: [
Container(
color: Colors.red,
width: childSize.width,
height: childSize.height
),
Container(
color: Colors.green,
width: childSize.width,
height: childSize.height,
),
],
);
}
}
class _TwoChildrenLayoutDelegate extends FlowDelegate {
const _TwoChildrenLayoutDelegate();
@override
void paintChildren(FlowPaintingContext context) {
double dx = 0, dy = 0;
for (int i = 0; i < context.childCount; ++i) {
context.paintChild(i, transform: Matrix4.translationValues(dx, dy, 0));
if (context.size.height > context.size.width) {
dy = context.getChildSize(i)!.height;
} else {
dx = context.getChildSize(i)!.width;
}
}
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) => false;
}
Natürlich braucht man kein Delegate für die Lösung der Aufgabe. z. B.:
import 'package:flutter/material.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(
home: Scaffold(
body: ExampleWidget(),
),
);
}
class ExampleWidget extends StatelessWidget {
const ExampleWidget({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final useVerticalLayout = constraints.maxHeight > constraints.maxWidth;
return Flex(
direction: useVerticalLayout ? Axis.vertical : Axis.horizontal,
children: [
Expanded(child: Container(color: Colors.red)),
Expanded(child: Container(color: Colors.green)),
],
);
},
);
}
}