How to remove padding from some children in a Column? - flutter

Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
// ... some widgets
Padding(
padding: const EdgeInsets.all(-20.0), // Error: How to do something like this?
child: FooWidget()
),
// ... more widgets
BarWidget(), // Remove padding from here also
// ... and some more widgets
],
),
)
I'm providing a padding of 20 to my Column, but I want to remove this padding from some of its children, like FooWidget, BarWidget, etc. How can I do that?
Note: I'm not looking for workarounds like provide the padding to other widgets instead of the root Column or wrap those widgets in another Column and provide that column a padding, etc.

you can apply transformation to the widgets that you want to remove the padding for, for example:
Container(
transform: Matrix4.translationValues(-20.0, 0, 0.0), //here
child: FooWidget()),

This working solution uses UnconstrainedBox that only takes away the left side and right side of padding. You might do the calculation of overflowWidth first when screenWidth is not feasible to use.
In addition, this comes up with a RenderConstraintsTransformBox overflowed exception that will be gone away in app release version, e.g. flutter build appbundle for android app.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: UnboundedWidget(),
);
}
}
class UnboundedWidget extends StatelessWidget {
const UnboundedWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final double overflowWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: const Text('Unbounded demo'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
UnconstrainedBox(
child: Container(
color: Colors.red,
width: overflowWidth,
child: const Text('123'),
),
),
],
)),
);
}
}

There is no such thing as negative margins in flutter.
You can try workarounds with transforms on x, y, z axis as transform property on Container widget.
Or try with SizedBox which ignores parent padding.
Here is a similar example that should work:
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Container(
width: double.infinity, height: 20, color: Colors.green),
// This child ignores parent padding.
SizedBox(
width: MediaQuery.of(context).size.width,
height: 20,
child: Expanded(
child: OverflowBox(
maxWidth: MediaQuery.of(context).size.width,
child: Container(
width: double.infinity,
height: 20,
color: Colors.red)),
),
),
Container(
width: double.infinity,
height: 20,
color: Colors.blue),
],
),
),

Use Stack widget with Positioned widget Positioned(left: -20, child: widget)
on the other hand, for padding less.
you can create custom 2 widget name as:
paddingLessWidget(child: your widget)
paddingWithWidget(child: your widget)
then use this into column() widget.
Remove padding from column's parents.
use as:
Column(
children:[
paddingLessWidget(child: your widget),
paddingWithWidget(child: your widget)
]
),

Related

Flutter : dynamic height for scrollable content inside customScrollView (with sticky behaviors)

I would like to achieve this result but I can't get my head around it...
Image exemple
The only way I found to "fixed" my header (top) and button (bottom) was to use respectively SliverPinnedHeader (which requires the user of the CustomScrollView widget) and StickySideWidget.bottom.
As I am inside a Column widget, I am required to use the Expanded widget which make the scroll works when the list is long enough, but that also take full screen height even when I only have a few items... I'd like to find a way to remove the Expanded without breaking everything
Here is a simplified code :
import 'package:flutter/material.dart';
import 'package:shared/widgets/commons/safe_area.dart';
import 'package:sliver_tools/sliver_tools.dart';
class DemoWidget extends StatelessWidget {
DemoWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text('Demo'),
onPressed: () => showModalBottomSheet(
backgroundColor: Colors.white,
isScrollControlled: true,
context: context,
builder: (context) => DemoContent(),
))));
}
}
class DemoContent extends StatelessWidget {
DemoContent({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final items = List.filled(20, '');
final bottomSheetClosingLine = Padding(
padding: const EdgeInsets.only(bottom: 32),
child: Center(child: Container(color: Colors.grey, height: 4, width: 64)),
);
final header = Container(
padding: const EdgeInsets.all(24),
color: Colors.blue,
child: Text('Header'));
final itemWidget = Padding(
padding: const EdgeInsets.symmetric(vertical: 16), child: Text('item'));
final button = Container(
padding: EdgeInsets.only(top: 24),
width: double.infinity,
child: ElevatedButton(onPressed: () {}, child: Text('Save')),
);
return SafeArea(
child: Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.80),
padding: EdgeInsets.all(24),
width: double.infinity,
child: Column(mainAxisSize: MainAxisSize.min, children: [
bottomSheetClosingLine,
Expanded(
child: StickySideWidget.bottom( /// Custom widget
body: CustomScrollView(slivers: [
SliverPinnedHeader(child: header),
SliverToBoxAdapter(
child: Column(
children: items.map((item) => itemWidget).toList(),
))
]),
side: button,
))
])));
}
}
Edit : I would also like to avoid as much as possible to add an unnecessary library...
Try to use this Widget
draggableScrollableSheet

I am new to flutter and I think I have misunderstood the widget and layout system

If I want to add more buttons and text widgets where and how should I do it, should I make some sort of column and row system or am I totally off? And is my code programmed wrong?
import 'package:flutter/material.dart';
void main() {
runApp(
const HomeScreen(),
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Dice'),
centerTitle: true,
),
body: const Dice()));
}
}
class Dice extends StatefulWidget {
const Dice({Key? key}) : super(key: key);
#override
State<Dice> createState() => _DiceState();
}
class _DiceState extends State<Dice> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
children: [
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/1.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/2.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/3.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/4.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/5.png')))
],
),
));
}
}
I am going to add variables to the children in the container later.
For better understanding I would recommend you checking out this medium article. https://medium.com/flutter-community/flutter-layout-cheat-sheet-5363348d037e Here you can view all the important layout widgets you can use.
In general you have a widget tree starting by MaterialApp and you can add as many items as you want. In flutter if you want multiple widgets you can use Row and Column for that. Both of them provides a children property in brackets [] there you can add multiple widgets inside separated by comma.
Most of the other widgets are also able to provide a children property where you can add even more children widgets. That's how the widget tree in general works. Actually you have unlimited possibility’s.
Your code is totally fine. By the way you can also create custom widgets if the plenty widgets of flutter doesn't fit your use case.
In your example you can add whatever you want in your row, text, more images, buttons, everything you like.
Here you can check out the official widget catalog of flutter: https://docs.flutter.dev/development/ui/widgets
You can place a Column/ListView in between your Center and Row widgets, then you could add multiple rows to it like below:
class _DiceState extends State<Dice> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Row(
children: [
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/1.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/2.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/3.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/4.png'))),
Expanded(
child: Container(
margin: const EdgeInsets.all(15),
child: Image.asset('images/5.png')))
],
),
Row(children: [/* more content here */]),
],
),
),
);
}
}

My ConstrainedBox does not maintain the minimum size dimension

I get a box with a card and centered text but when I decrease the size of the window my card disappears and my text overflows. I get an overflow error when the box gets smaller than the text.
I want the smallest box dimension to be 300x300 and the largest box to be 600x600 rather than shrinking indefinitely
Maybe adding a Singlechildscrollview is the best I can get. I still think there is a way to create a shrinking card up to a certain minimum dimension
class TestConstrainedBox extends StatefulWidget {
TestConstrainedBox({Key? key}) : super(key: key);
#override
State<TestConstrainedBox> createState() => _TestConstrainedBoxState();
}
class _TestConstrainedBoxState extends State<TestConstrainedBox> {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title:Text("Test Constrained Box")),body:
SingleChildScrollView(child:
Container(
margin: const EdgeInsets.only(top:20.0, left: 20.0, right: 20.0, bottom:10.0),
child:
SizedBox.fromSize(size: const Size(450,450),
//OverflowBox(minHeight:300,minWidth:300,
//maxWidth:300, maxHeight: 300,
child:ConstrainedBox(constraints: BoxConstraints(
minWidth:300,
minHeight:300,
maxWidth:350,
maxHeight:350,
),
child:
Card(child:
Column(mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children:[
//SizedBox(width:350, height:350, child:
Text('Hello World!')
//)
]))
))
)
)
);
}
}
As said in official documentation. (https://api.flutter.dev/flutter/widgets/SizedBox-class.html)
If given a child, this widget forces it to have a specific width and/or height. These values will be ignored if this widget's parent does not permit them. For example, this happens if the parent is the screen (forces the child to be the same size as the parent), or another SizedBox (forces its child to have a specific width and/or height). 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.
so you have to either remove sizedBox or Wrap ConstrainedBox with center.
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(MaterialApp(
home: TestConstrainedBox(),
));
class TestConstrainedBox extends StatefulWidget {
TestConstrainedBox({Key? key}) : super(key: key);
#override
State<TestConstrainedBox> createState() => _TestConstrainedBoxState();
}
class _TestConstrainedBoxState extends State<TestConstrainedBox> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Test Constrained Box")),
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.only(
top: 20.0, left: 20.0, right: 20.0, bottom: 10.0),
child: SizedBox.fromSize(
size: const Size(450, 450),
//OverflowBox(minHeight:300,minWidth:300,
//maxWidth:300, maxHeight: 300,
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: 300,
minHeight: 300,
maxWidth: 350,
maxHeight: 350,
),
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//SizedBox(width:350, height:350, child:
Text('Hello World!')
//)
],
),
),
),
),
),
),
),
);
}
}

Best way to define a bespoke Card in Flutter

I've been attempting to define a bespoke Card in Flutter using row and column and cannot seem to get a fixed format layout similar to the image above (the red lines denote the areas of the card and are just there to show the areas).
e.g.
return Card(child: Column(
children: [
Row(
children: [
Column( children: [
Text('Riverside cafe...'),
Ratingbar(),
],),
ImageWidget(),
],
),
Container(child: Text('Pubs & restaurants'), color : Colors.purple)
],
The resulting cards are to be displayed in a listview and using rows and columns above results in the areas being different sized depending on the data.
It seems to me that using row and column may not be the best way to achieve this. Is there a better way?
As for the best, I suppose that's for you and your client to decide.
For as long as I've been working with Flutter, I haven't come across anything like CSS grid which is great for situations like this. The closest comparison is StaggeredGrid (https://pub.dev/packages/flutter_staggered_grid_view) but that doesn't offer as much control as CSS grid and doesn't seem to quite fit your use case.
Rows, Columns (and other layout widgets) can get the job done:
Here's the main.dart that produced the above example. Code quality isn't perfect, but hopefully you can follow it well enough and it helps you get done what you need to get done.
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
static const String _title = 'Bespoke card example';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Bespoke card example')),
body: Center(
child: Wrap(runSpacing: 10.0, children: [
BespokeCard(title: 'Short name', width: 350),
BespokeCard(
title: 'Riverside Cafe with a really long name', width: 350)
]),
),
);
}
}
class BespokeCard extends StatelessWidget {
final String title;
final double width;
BespokeCard({this.title, this.width});
#override
Widget build(BuildContext context) {
Widget _restaurantNameContainer = Container(
constraints: BoxConstraints(
minHeight: 0,
maxHeight: 120,
maxWidth: (500.0 - 40 - 175 + 1),
minWidth: (500.0 - 40 - 175 + 1),
),
child: AutoSizeText(
title,
style: TextStyle(fontSize: 60),
maxLines: 2,
minFontSize: 10,
stepGranularity: 0.1,
overflow: TextOverflow.ellipsis,
),
);
Widget _rightSideSection = Container(
width: 175,
height: Size.infinite.height,
child: Center(
child: Icon(
Icons.umbrella,
size: 70,
),
),
);
Widget _topSection = Flexible(
flex: 1,
child: Row(
children: [
Flexible(
fit: FlexFit.tight,
flex: 3,
child: Padding(
padding: EdgeInsets.only(left: 40.0, top: 25.0),
child: Column(
children: [
Flexible(child: Container(), flex: 1),
_restaurantNameContainer,
Text('* * * * *', style: TextStyle(fontSize: 70)),
],
),
),
),
_rightSideSection
],
),
);
Widget _bottomSection = Container(
height: 70,
width: Size.infinite.width,
child: Center(
child: Text('Pubs & Restaurants',
style: TextStyle(color: Colors.white, fontSize: 40)),
),
color: Colors.purple);
Widget unfittedCard = Card(
child: SizedBox(
width: 500,
height: 300,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [_topSection, _bottomSection],
),
));
return Container(
width: this.width,
child: FittedBox(fit: BoxFit.fitWidth, child: unfittedCard));
}
}
NOTES:
Be aware of flexFit (tight or loose) property: https://api.flutter.dev/flutter/widgets/Flexible/fit.html
You can either define fixed ratios with all flexibles, or you can mix Flexibles with Containers / SizedBoxes what have you
The package auto_size_text is great for situations like this. (Add auto_size_text: ^2.1.0 to your dependencies)
Be aware of box constraints. I needed them to make the title autosizing text be able to grow tall without also sitting in a large container.
Fitted box is really handy and makes scaling very easy in flutter.

Expanded with max width / height?

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
),
),
),
);
}
}