-
Notifications
You must be signed in to change notification settings - Fork 29.5k
Description
In some cases it can be useful to relatively position a child to its parent.
For example let's say we want a container positioned at 10% from the left edge of its parent, 20% from the top, 30% from the right and 40% from the bottom:
As discussed in #24475, we can use a combination of Align and FractionallySizedBox for this, but this is not straightforward:
import 'package:flutter/material.dart';
void main() => runApp(MyDemoWidget());
class MyDemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// The child's left edge should be indented at 10% of the parent's width.
const double leftFactor = 0.10;
// The child's top edge should be indented at 20% of the parent's height.
const double topFactor = 0.20;
// The child should have a width of 60% (100 - 10 - 30) of the parent's width.
const double widthFactor = 0.60;
// The child should have a height of 40% (100 - 20 - 40) of the parent's height.
const double heightFactor = 0.40;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('DEMO'),
),
body: Center(
child: Container(
height: 100.0,
width: 100.0,
color: Colors.orange,
child: Align(
alignment: FractionalOffset(
leftFactor / (1.0 - widthFactor),
topFactor / (1.0 - heightFactor),
),
child: FractionallySizedBox(
widthFactor: widthFactor,
heightFactor: heightFactor,
child: Container(
color: Colors.yellow,
),
),
),
),
),
),
);
}
}As @goderbauer suggested, we could encapsulate this into a widget.
I ended up with this base of work (in order to have the same kind of API than the Positioned widget):
import 'package:flutter/widgets.dart';
class FractionallyAlignedSizedBox extends StatelessWidget {
FractionallyAlignedSizedBox({
Key key,
@required this.child,
this.leftFactor,
this.topFactor,
this.rightFactor,
this.bottomFactor,
this.widthFactor,
this.heightFactor,
}) : assert(
leftFactor == null || rightFactor == null || widthFactor == null),
assert(
topFactor == null || bottomFactor == null || heightFactor == null),
assert(widthFactor == null || widthFactor >= 0.0),
assert(heightFactor == null || heightFactor >= 0.0),
super(key: key);
final double leftFactor;
final double topFactor;
final double rightFactor;
final double bottomFactor;
final double widthFactor;
final double heightFactor;
final Widget child;
@override
Widget build(BuildContext context) {
double dx = 0;
double dy = 0;
double width = widthFactor;
double height = heightFactor;
if (widthFactor == null) {
final left = leftFactor ?? 0;
final right = rightFactor ?? 0;
width = 1 - left - right;
if (width != 1) {
dx = left / (1.0 - width);
}
}
if (heightFactor == null) {
final top = topFactor ?? 0;
final bottom = bottomFactor ?? 0;
height = 1 - top - bottom;
if (height != 1) {
dy = top / (1.0 - height);
}
}
if (widthFactor != null && widthFactor != 1) {
if (leftFactor != null) {
dx = leftFactor / (1 - widthFactor);
} else if (leftFactor == null && rightFactor != null) {
dx = (1 - widthFactor - rightFactor) / (1 - widthFactor);
}
}
if (heightFactor != null && heightFactor != 1) {
if (topFactor != null) {
dy = topFactor / (1 - heightFactor);
} else if (topFactor == null && bottomFactor != null) {
dy = (1 - heightFactor - bottomFactor) / (1 - heightFactor);
}
}
return Align(
alignment: FractionalOffset(
dx,
dy,
),
child: FractionallySizedBox(
widthFactor: width,
heightFactor: height,
child: child,
),
);
}
}So that the previous example could be written like this:
import 'package:flutter/material.dart';
void main() => runApp(MyDemoWidget());
class MyDemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('DEMO'),
),
body: Center(
child: Container(
height: 100.0,
width: 100.0,
color: Colors.orange,
child: FractionallyAlignedSizedBox(
leftFactor: 0.10,
topFactor: 0.20,
rightFactor: 0.30,
bottomFactor: 0.40,
child: Container(
color: Colors.yellow,
),
),
),
),
),
);
}
}Concerning the name FractionallyAlignedSizedBox I'm not sure if people who look for positioning a widget relative to the size of its parent would find it easily 😕.
What do you think about this?
