There are two main widgets:
I have this OverflowingWidget that is basically a Column - in reality this contains many widgets - However for this example, it has been given a height bigger than its parent widget.
The Parent widget ContainerWidget is basically AnimatedSwitcher which is basically a Stack contained by Column which constrained by a hight less than OverflowingWidget.
As you can see in dart-pad there is an overflow. The OverflowingWidget.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
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: ContainerWidget(),
),
),
);
}
}
class ContainerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: 100,
width: 50,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: ClipRect(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: OverflowingWidget(),
layoutBuilder: (currentChild, previousChildren) => Stack(
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
)),
),
),
],
));
}
}
class OverflowingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [Container(color: Colors.blue, height: 150, width: 50)]);
}
}
Desired result:
I need the OverflowingWidget to be clean clipped without trying to resize the content inside. The same effect that can be obtained when in css:overflow:hidden.
AnimatedSwitcher is getting height 100, but it needs 150 for current widget. you can increase the height on redContainer.
Instead of using Column, you can use Stack widget.
class ContainerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: 150,
width: 50,
child: Stack(
children: [
ClipRect(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: OverflowingWidget(),
layoutBuilder: (currentChild, previousChildren) => Stack(
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
)),
),
],
));
}
}
I found a way to clip an overflowing column/row. Simply wrap that column/row with Wrap widget. That will do the clipping.
class OverflowingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Wrap(children: [
Column(children: [
Container(
color: Colors.blue,
height: 150,
width: 50)
])
]);
}
}
Related
Let's say that I want create a Flutter app. In my app I want to create a Row widget with the following children:
AspectRatio(aspectRatio: 1, child: Center(child: Text("I am text!"))
Image.asset("path/to/asset.png") with unknown aspect ratio
I want to display that Row while keeping both aspect ratios intact. How can I do that?
Here is my attempt:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("This is my app")),
body: Container(
child: Row(children: [buildItem1(), buildItem2()]),
),
);
}
Widget buildItem1() {
return AspectRatio(
aspectRatio: 1,
child: Container(
color: Colors.red, child: Center(child: Text("This is my text"))));
}
Widget buildItem2() {
return Image.asset("path/to/my/asset.jpg");
}
}
And this is what being displayed:
I am trying to figure out a way to automatically shrink the row height such that both items would fit the screen.
If I use a fixed hight like that:
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("This is my app")),
body: Container(
height: 300,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [buildItem1(), buildItem2()],
),
),
);
}
...
Than I get the following:
Which is better, but I want to fill the entire width of the screen.
Thanks for anyone who can help!
You can use Expanded on second widget, that will get the avialable space on row. Also for the image use fit: BoxFit.cover(better looks than width).
body: Container(
height: 300,
child: Row(
children: [buildItem1(), Expanded(child: buildItem2())],
),
),
Widget buildItem2() {
return Image.asset(
"assets/images/user.png",
fit: BoxFit.cover,
);
}
Now if you want full screen, I will prefer
Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
color: Colors.red,
child: Center(
child: Text("This is my text"),
)),
),
Expanded(
child: Image.asset(
"assets/images/user.png",
fit: BoxFit.cover,
)),
],
),
Also LayoutBuilder on scaffold's body provide more options. Also check IntrinsicHeight.
I am trying to achieve an effect where there is expandable content on the top end of a sidebar, and other links on the bottom of the sidebar. When the content on the top expands to the point it needs to scroll, the bottom links should scroll in the same view.
Here is an example of what I am trying to do, except that it does not scroll. If I wrap a scrollable view around the column, that won't work with the spacer or expanded that is needed to keep the bottom links on bottom:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
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: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
State<MyWidget> createState() {
return MyWidgetState();
}
}
class MyWidgetState extends State<MyWidget> {
List<int> items = [1];
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
items.add(items.last + 1);
});
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
if (items.length != 1) items.removeLast();
});
},
),
],
),
for (final item in items)
MyAnimatedWidget(
child: SizedBox(
height: 200,
child: Center(
child: Text('Top content item $item'),
),
),
),
Spacer(),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all()),
height: 200,
child: Text('Bottom content'),
)
],
);
}
}
class MyAnimatedWidget extends StatefulWidget {
final Widget? child;
const MyAnimatedWidget({this.child, Key? key}) : super(key: key);
#override
State<MyAnimatedWidget> createState() {
return MyAnimatedWidgetState();
}
}
class MyAnimatedWidgetState extends State<MyAnimatedWidget>
with SingleTickerProviderStateMixin {
late AnimationController controller;
#override
initState() {
controller = AnimationController(
value: 0, duration: const Duration(seconds: 1), vsync: this);
controller.animateTo(1, curve: Curves.linear);
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return SizedBox(height: 200 * controller.value, child: widget.child);
});
}
}
I have tried using a global key to get the size of the spacer and detect after rebuilds whether the spacer has been sized to 0, and if so, re-build the entire widget as a list view (without the spacer) instead of a column. You also need to listen in that case for if the size shrinks and it needs to become a column again, it seemed to make the performance noticeably worse, it was tricky to save the state when switching between column/listview, and it seemed not the best way to solve the problem.
Any ideas?
Try implementing this solution I've just created without the animation you have. Is a scrollable area at the top and a persistent footer.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
home: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("My AppBar"),
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
// Your scrollable widgets here
Container(
height: 100,
color: Colors.green,
),
Container(
height: 100,
color: Colors.blue,
),
Container(
height: 100,
color: Colors.red,
),
],
),
),
),
Container(
child: Text(
'Your footer',
),
color: Colors.blueGrey,
height: 200,
width: double.infinity,
)
],
),
),
),
);
}
}
I want widgets that has certain size but shrink if available space is too small for them to fit.
Let's say available space is 100px, and each of child widgets are 10px in width.
Say parent's size got smaller to 90px due to resize.
By default, if there are 10 childs, the 10th child will not be rendered as it overflows.
In this case, I want these 10 childs to shrink in even manner so every childs become 9px in width to fit inside parent as whole.
And even if available size is bigger than 100px, they keep their size.
Wonder if there's any way I can achieve this.
return Expanded(
child: Row(
children: [
...List.generate(Navigation().state.length * 2, (index) => index % 2 == 0 ? Flexible(child: _Tab(index: index ~/ 2, refresh: refresh)) : _Seperator(index: index)),
Expanded(child: Container(color: ColorScheme.brightness_0))
]
)
);
...
_Tab({ required this.index, required this.refresh }) : super(
constraints: BoxConstraints(minWidth: 120, maxWidth: 200, minHeight: 35, maxHeight: 35),
...
you need to change Expanded to Flexible
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(appBar: AppBar(), body: Body()),
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 80,
color: Colors.green,
child: Row(
children: List.generate(10, (i) {
return Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 10, maxHeight: 10),
foregroundDecoration: BoxDecoration(border: Border.all(color: Colors.yellow, width: 1)),
),
);
}),
),
);
}
}
two cases below
when the row > 100 and row < 100
optional you can add mainAxisAlignment property to Row e.g.
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Try this
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 10,maxHeigth:10),
child: ChildWidget(...),
)
The key lies in a combination of using Flexible around each child in the column, and setting the child's max size using BoxContraints.loose()
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Make them fit',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int theHeight = 100;
void _incrementCounter() {
setState(() {
theHeight += 10;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Playing with making it fit'),
),
body: Container(
color: Colors.blue,
child: Padding(
// Make the space we are working with have a visible outer border area
padding: const EdgeInsets.all(8.0),
child: Container(
height: 400, // Fix the area we work in for the sake of the example
child: Column(
children: [
Expanded(
child: Column(
children: [
Flexible(child: SomeBox('A')),
Flexible(child: SomeBox('A')),
Flexible(child: SomeBox('BB')),
Flexible(child: SomeBox('CCC')),
Flexible(
child: SomeBox('DDDD', maxHeight: 25),
// use a flex value to preserve ratios.
),
Flexible(child: SomeBox('EEEEE')),
],
),
),
Container(
height: theHeight.toDouble(), // This will change to take up more space
color: Colors.deepPurpleAccent, // Make it stand out
child: Center(
// Child column will get Cross axis alighnment and stretch.
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Press (+) to increase the size of this area'),
Text('$theHeight'),
],
),
),
)
],
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class SomeBox extends StatelessWidget {
final String label;
final double
maxHeight; // Allow the parent to control the max size of each child
const SomeBox(
this.label, {
Key key,
this.maxHeight = 45,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ConstrainedBox(
// Creates box constraints that forbid sizes larger than the given size.
constraints: BoxConstraints.loose(Size(double.infinity, maxHeight)),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: BoxDecoration(
color: Colors.green,
border: Border.all(
// Make individual "child" widgets outlined
color: Colors.red,
width: 2,
),
),
key: Key(label),
child: Center(
child: Text(
label), // pass a child widget in stead to make this generic
),
),
),
);
}
}
I first want to center one widget and then add another widget to the right of the center widget.
Currently, flutter APIs are not feasible for this. In android, it can be easily done in relative layout or constraint layout.
I tried with some hack with MediaQuery,
Stack(
children: [
Center(child: Text(17)),
Positioned(
bottom: 0,
left: (screenWidth / 2) + (screenWidth / 3),
child: Column(
children: []
)
)
How to try this in an easy way or a proper way.
You can use the Align widget for this.
Code Example
import 'package:flutter/material.dart';
class SandBox extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 300,
height: 300,
color: Colors.blue,
child: Align(
alignment: Alignment.centerRight, //aligns the child widget to the right center
child: Text('17'),
),
),
),
);
}
}
Maybe, it can help you to achieve what you want:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text('17'),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
),
),
],
),
);
}
}
Or you can adjust it using some calculations using MediaQuery and Stack.
You can use badges package to achieve this goal.
Finally, I found used https://pub.dev/packages/align_positioned
AlignPositioned.relative(
Text(day.englishDate.toString(),
textScaleFactor: 3, style: dayTextStyle),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
....
],
),
moveByChildWidth: 1.1,
minChildWidthRatio: 1,
moveByChildHeight: 0.3)
Please use Align along with Row.
here is simple code to achieve this.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
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: Stack(
children:[
Align(
child : Row(
mainAxisAlignment: MainAxisAlignment.center,
children:[
Padding(child: Text('Left'),padding: EdgeInsets.all(5)),
Text('Center widget'),
Padding(child: Text('Right'),padding: EdgeInsets.all(5))
]) ,
alignment: Alignment.center),
]
),
),
);
}
}
Here is the output:
I want to place a paint board widget at the center of the screen and a clear button that sits top right above the paint board. I have tried column with MainAxisAlignment.center but it centers base on the board and clear button so the board is not absolutely at the center. How can I achieve this effect?
You can use positioned with stack and place where ever you want. copy and try this code in dartpad.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
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: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 100,
child: Container(
width: MediaQuery.of(context).size.width,
height: 250, color: Colors.yellow)),
Positioned(
top: 60,
right: 20,
child: Icon(Icons.close, size: 32)),
]
);
}
}
Here is a similar UI with a Column widget, you can add a SizedBox below the yellow box inside the Column with the same height as the Icon, so that the yellow box appears to be centred.
So, we can avoid hardcoded values inside thr Positioned widget (that impacts responsive UI)
Here is the code snippet.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
double iconSize = 32;
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Container(
alignment: Alignment.centerRight,
height: iconSize,
child: Icon(Icons.close, size: iconSize),
),
Container(
width: MediaQuery.of(context).size.width,
height: 250,
color: Colors.yellow,
),
SizedBox(height: iconSize)
]);
}
}