Flutter: Can I use Ink in Dismissible? - flutter

I'm using Ink widget with decoration to allow ink splashes above images and colored background.
After I wrapped it inside Dismissible, I got a wierd effect: When I swipe the widget, it's content moves as expected but the decoration stucks in it's original position.
You can see this live in dartpad: https://dartpad.dev/5ef2d2eb3823821a74aa11c680d84d4b?null_safety=true
Q: Is this an intended behaviour in flutter or is it a bug?
Note: The issue disappears if I replace Ink with Container or if I put it out of SingleChildScrollView.
A code to reproduce:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
// remove SingleChildScrollView and all will be fine
body: SingleChildScrollView(
child: Dismissible(
key: Key('1'),
// change Ink to Container and all will be fine
child: Ink(
width: 100,
height: 100,
color: Colors.red,
child: Text('Swipe me, and watch my background get stuck!'),
),
),
),
),
);
}
}

The documentation of Ink is...
Paints a decoration (which can be a simple color) on a [Material].
It happens in your sample code because it colors the MaterialApp. To fix your issue, wrap the Ink inside a Material.
Sample...
Material(
child: Ink(
width: 100,
height: 100,
color: Colors.red,
child: Text('Swipe me, and watch my background get stuck!'),
),
),

Related

How to control size of a Column inside a SizedBox

I am building a widget to display a chart/graph inside a fixed size window. The chart will be bigger than the window so the solution includes the user being able to scroll the graph widgets around inside a window (you can find details about this in an earlier question I asked about this here).
Part of the layout includes a fixed sized panel created using a SizedBox and, within that, a Column containing rows of widgets that make up the graph. I need the Column to fit its contents tightly so that I can track it's size and, for example, stop the user scrolling up when the last row is visible at the bottom of the SizedBox.
When the size of the children in the Column should make the Column smaller than the SizedBox, the Column is still being forced to be the size of the SizedBox. This is explained in the Flutter documentation here.
According to the Flutter documentation, the solution is:
This can be remedied by wrapping the child SizedBox in a widget that
does permit it to be any size up to the size of the parent, such as
Center or Align.
I have tried this and it doesn't seem to work. Below is a test app I wrote on DartPad to check this out. If I use either Align or Center as child of SizedBox and parent of the Column widget, the Column is still the same size as the SizedBox. I have also added MainAxisSize.min to the Column, but this doesn't appear to make any difference.
I have considered doing this using a Stack so that the Column is displayed over the SizedBox, rather than as a child of it, but that feels like a bit of a hacky workaround given that the documentation suggests you can control the size of a Column inside a SizedBox.
Does anyone know how I force the Column to be the smallest size it can be inside a SizedBox of fixed size?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: SizedBox(
// This is the window the chart is displayed in
height: 200,
width: 400,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Center(
// This should allow the Column to be smaller than the SizedBox?
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
),
);
}
}
As per SayyidJ's reply, the answer is to use a ConstrainedBox rather than a SizedBox. The Column can shrink to fit the contents when it is the child of a ConstrainedBox.
Here is the revised code, which you can run in DartPad, showing this working.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ConstrainedBox(
// This is the window the chart is displayed in
constraints: BoxConstraints(
maxHeight: 200,
maxWidth: 400,
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
);
}
}

Flutter : How to color Sizedbox (without using container)

1. Explanation
I want to change full color of 'SizedBox' to see the location and scope of 'SizedBox'. I wonder is there any way to change SizedBox's color without filling it with Container or Decoration box. If not, I want to know how to fill SizedBox with decoration box.
2. Code
Here is a code that I tried.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home : Scaffold(
appBar: AppBar(
title : const Text('this is title'),
),
body : const SizedBox(
width : 200,
child: DecoratedBox(
decoration: BoxDecoration(
color : Colors.red,
),
),
),
bottomNavigationBar: (
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Icon(Icons.favorite),
Icon(Icons.home),
Icon(Icons.settings)
],
)
),
),
);
}
}
**3. Result **
Here is what I got from the code.
You can wrap your SizedBox with ColoredBox
ColoredBox(
color: Colors.cyanAccent,
child: SizedBox(
width: 200,
height: 100,),
),
You can try ColoredBox widget
SizedBox(
width : 200,
height: 20,
child: ColoredBox(color: Colors.amber),
),
If you'd like to decorate the SizedBox to see the location and scope of the Widget just for debugging purposes, we can enable the debugPaintSizeEnabled just by pressing p on the CLI upon launching the flutter run command.
Otherwise, using a Container with explicit size parameters it's mostly the same as using SizedBox, and it would allow you to use have a background color or border decoration.
Another option would be to use a ColoredBox as the child of the SizedBox, or vice versa.
I'm not sure why you'd use it but you could have the child of the SizedBox be a container with the color you want. The container will stretch to the sizes provided
SizedBox(
height: 10,
width: 30,
child: Container(color: Colors.red),
),
Also you could use the widget inspector provided by flutter:
https://docs.flutter.dev/development/tools/devtools/inspector#:~:text=The%20Flutter%20widget%20inspector%20is,%2C%20rows%2C%20and%20columns).

Why doesn't Container respect the size it was given?

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: TestCode(),
);
}
}
class TestCode extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 100,
color: Colors.red,
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
);
}
}
In fact, this code is very simple. I just want to display a 200 * 100 red cube and a 100 * 100 green cube.
But the running effect is full screen green? Why is that?
Next, I added a Scaffold toTestCode, as follows
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(body: TestCode()),
);
}
The effect again seems to be closer, showing a 200 * 100 green cuboid? Why is that?
Next, I added an alignment to the first Container, as follows
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.topLeft,
width: 200,
height: 100,
color: Colors.red,
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
);
}
Finally achieved the desired effect, why is this, who can explain, I must figure this out.
This is caused by what we call "tight vs loose constraints" in Flutter.
TD;DR, width/height are tight constraints (in the sense that there's only a single possibility).
But you never specified to the framework how to switch between the tight constraint of 200x200 specified by the parent, to the tight constraint of 100x100 of the child.
This cause a constraint conflict. Both widgets have a single possibility, and there's nothing that allows both to live together (like an alignment).
In that situation, the constraints of the parent always win, and we therefore end up with a 200x200 square where the child fills its parent.
If that is not what you want; then you should transform your "tight" constraint into a "loose" constraint.
A loose constraint is a constraint that offer the child multiple possibilities, which usually remove the conflict.
The most common way to introduce a loose constraint is to use Alignment (or Center or the alignment property of Container).
As such, if you write:
Container(
width: 200,
height: 100,
color: Colors.red,
child: Center(
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
),
);
then in that situation, Center will act as a middle ground between the parent and child Container.
It will understand that both wants a different size. And it will solve the conflict by aligning the child in the parent.
Now, why is this desired you may ask? Why can't Flutter implicitly add an alignment here?
That is because in many situations, this behavior is desired.
Basically, this ensures that there's always a way to customize the size of something (without having to expose tons of properties on all widgets).
Take RaisedButton as an example. It doesn't expose anything to change its size, but we may want it to fill the screen.
In that situation we'd write:
SizedBox.expand(
child: RaisedButton(...),
)
Because of the behavior we explained previously with the parent overriding the child size when there's a conflict, this code will produce a RaisedButton that properly fills the screen.
height and width properties getting overrided. You can have more info about box constraints on this article:
Dealing with box constraints
Flutter has bunch of layout widgets that can get the job done. In your case you gave Container to the home property of MaterialApp. This set the minimum size of the Container to the screen size. MaterialApp wants his child to fill all the screen in order to prevent any black pixels. This is an expected behaviour. However you can use a layout widget that can break this constraint, it may be Center , FittedBox or else.
An example with FittedBox:
class TestCode extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FittedBox(
fit: BoxFit.scaleDown,
child: Container(
width: 200,
height: 100,
color: Colors.red,
child: FittedBox(
fit: BoxFit.scaleDown,
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
),
),
);
}
}
Output:
Always remember that the container by default takes the dimensions of the parent, and it's a good practice to wrap all your elements/widgets in a root top level Container and then wrap each container in the widget tree with widgets that position the desired Container (Center can be such a widget)
An easy work-around solution for your problem would be:
// The root container
Container(
//Center for positioning the child Container properly
child: Center(
child: Container(
height: 100,
width: 200,
color: Colors.red,
//Center for positioning the child Container properly
child: Center(
child: Container(
height: 100,
width: 100,
color: Colors.green,
),
),
),
),
),
Just change your code into this. You have to specify alignment.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: TestCode(),
);
}
}
class TestCode extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 200.0,
height: 100.0,
color: Colors.red,
alignment: Alignment.center, // where to position the child
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green,
),
);
}
}

How to make relative layout in flutter app

Please see this image -https://ibb.co/YpjJsLZ
In above image how can I place burger image to center of white view.
I tried using Align & Center widget but its no fitting properly.
How can relative layout works in flutter?
Flutter does not have layout types like android,
But you can place burger image using stack widget.
You can follow this tutorial for reference.
you can not use Relative Layout in Flutter but it Replace using "Stack".
The stack is a widget in Flutter that contains a list of widgets and positions them on top of the other.
stack allows developers to overlap multiple widgets into a single screen and renders them from bottom to top.
Hence, the first widget is the bottommost item, and the last widget is the topmost item.
import 'package:flutter/material.dart';
class StackWidget extends StatefulWidget {
#override
_StackWidgetState createState() => _StackWidgetState();
}
class _StackWidgetState extends State<StackWidget> {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Colors.yellow,
height: 300,
child: Stack(
alignment: Alignment.topLeft,
children: [
Container(
color: Colors.cyan,
width: 200,
height: 200,
),
Container(
color: Colors.red,
width: 150,
height: 150,
),
Container(
color: Colors.blue,
width: 100,
height: 100,
),
],
),
);
}
}
Output:
Done☻♥.

Flutter : Weird gap between items of ListView

I'm having a weird issue with Flutter and the ListView widget. I can see a gap between my items. I can see the black background. Is there something I'm missing so that these gaps does not show? Here is the whole application code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: Container(
color: Colors.black,
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
color: Colors.pink.shade100,
child: Container(
height: 100.0,
),
);
},
itemCount: 100,
),
),
);
}
}
Notice how the gaps are not always visible and change while scrolling.
I tried running your code but I didn't got any unexpected padding between List items. I'm currently on Flutter 2.5
You can try adding padding 0 on your ListView and see if it removes the unexpected padding.
child: ListView.builder(
padding: EdgeInsets.all(0.0),
...
),
I had the same issue, where the line would appear on the end of list view thus showing divider in last two Childs of the ListView.
Solutions I tried was :
setting padding of ListView as zero
setting addRepaintBoundary as false
(spoiler alert) both didn't work.
If all the children of list view are of same color, then wrap you ListView with container and set the color of the container to the color of list child. It will hide the lines.