How to structure a flutter project like this one:
Example restaurant pos image
Do you find this beginning of the tree structure correct:
class HomePageState extends State<HomePage>{
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Row(
children: [
Container( // menu
width:60,
color: Colors.white,
),
Expanded( // body
child: Container(
color: Colors.red,
),
),
Container( // ListProducts
width:300,
color: Colors.green,
),
],
),
backgroundColor: Color.fromARGB(255 , 244 , 246, 250),
)
);
}
}
code preview
You might want to place that MaterialApp into a separate parent widget (I think it will cause issues when using MediaQuery and Theme inside the same build method). It also might be cleaner down the line to extract every part (menu, body, ListProducts) into separate widgets.
Also, I would advise you to take a look at the LayoutBuilder widget,
and the ressources on this page if the app is meant to work on narrower screens.
Oh and if you don't know about state management, definitely check this out.
Related
I am learning Flutter, I want to achieve this look:
Does Container only allow one child? I want to have multiple of columns, like on the picture I will need 3 for logo, text box and for two buttons. How do I set this up properly? Maybe I should not use container?
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text("test"),
Text("test")
]
)
Also, what does this code do?
const MyApp({Key? key}) : super(key: key); I haven't seen that in any of the tutorials. is that some sort of constructor?
For the top logo part, you can simply use appBar of Scaffold.
Next comes the large box TextBox, you can use Expanded with Align widget inside column for this.
While we used Expanded it will take available height.
Therefore next two button will be at the bottom side.
I will suggest visiting and learn more about widgets, there are many ways you can handle this UI. You can search and read about every widget.
class TX extends StatelessWidget {
const TX({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Text("logo"),
),
body: LayoutBuilder(
builder: (context, constraints) => Column(
children: [
Expanded(
child: Container(
color: Colors.cyanAccent,
child: const Align(
alignment: Alignment(0, .5),
child: Text("TextBox"),
),
),
),
SizedBox(
width: constraints.maxWidth,
child: ElevatedButton(
onPressed: () {},
child: Text("Button"),
),
),
SizedBox(
height: 10,
),
SizedBox(
width: constraints.maxWidth,
child: ElevatedButton(
onPressed: () {},
child: Text("Buttonx"),
),
),
],
),
),
);
}
}
And for the key constructor already describe on comments by #Midhun MP. It is used to identify the widget tree. Check this video
Does Container only allow one child?
Yes, Container() can only have one child widget.
Widgets are like lego blocks. You have to pick a widget that best suits your requirements. For Showing widgets in a single column, You can use Column() Widget. Similarly in case of row, You can represent widgets in single Row using Row() Widget. Similarly for stacking of widgets, use Stack() widget. This list goes on just like the availablility of lego blocks.
Now back to your implementation, you are going the right way. You don't need Container() at the top, Just add 4 child widgets in Column.
Column(
children: [
Image(),
TextField(),
TextButton(),
TextButton(),
],
)
Study about the available customization options of these widgets and you will be able to implement this UI as per your requirements.
P.S. There are many type of buttons available in flutter. If TextButton() doesn't work for you, you can pick any other button.
The goal is to have a ListView that contains a multiline texfield (with an arbitrary number of lines, maxLines=null) that is followed by several ListTiles. When lines are added to the TextField, it grows and the ListTiles should move accordingly. However, there is an unexpected behaviour with the following code:
#override
Widget build(BuildContext context) {
ListView l = new ListView(children: [
TextField(maxLines: null),
SizedBox(height: 50, child: ColoredBox(color: Colors.yellow,child: Text("Tile1"),) ),
ListTile(tileColor: Colors.blueGrey,title: Text("Tile1"),),
ListTile(tileColor: Colors.blueGrey,title: Icon(Icons.looks_two_rounded),)
]);
return Scaffold(backgroundColor: Color(0xdcdcffff), body: Center(child: l));
}
https://gfycat.com/fr/obviousshorttermchihuahua
The green colored box moves down at expected but the ListTiles do not (although their children do), until I scroll, then they move where they should've been.
Is there any way to solve this ?
I don't know what exactly caused the bug, but I found a workaround that could perhaps be useful to someone trying to do the same: I put the ListTile in transparent inside a Colored box:
#override
Widget build(BuildContext context) {
ListView l = new ListView(children: [
TextField(maxLines: null),
SizedBox(
height: 50,
child: ColoredBox(
color: Colors.green,
child: Text("Tile1"),
)),
ColoredBox(
color: Color(0x77ff0000),
child: ListTile(
tileColor: Colors.transparent,
title: Text("Tile1"),
),
),
ColoredBox(
color: Color(0x77ff0000),
child: ListTile(
tileColor: Colors.transparent,
title: Icon(Icons.looks_two_rounded),
)),
]);
return Scaffold(body: Center(child: l));
}
https://gfycat.com/fr/idioticquarrelsomegossamerwingedbutterfly
I found some topics about calculating height in flutter, but noone of them answered my question. I am trying to calculate 100% of device height in flutter. I assume, I have to subtract from MediaQuery.of().size.height two things. First is AppBar height, so I calculated it by creating variable appBar and get property preferredSize.height. Second is the bar above appBar (which contains things like battery status, notifications etc.).
This is the simplest example:
#override
Widget build(BuildContext context) {
var appBar = AppBar(
title: Text(widget.title),
);
return Scaffold(
appBar: appBar,
body: Center(
child: Builder(
builder: (ctx) => Column(
children: <Widget>[
Container(
height: MediaQuery.of(ctx).size.height -
MediaQuery.of(ctx).padding.top -
appBar.preferredSize.height,
color: Colors.red,
)
],
),
),
),
);
}
This is my main Widget so I had to create context with Builder. However, it is not still equal to 100% of device height. It is 24px too much. I don't want to subtract 24px because It might be more/less on another device. What is wrong with this example?
You don't need to do anything special to the child of the Scaffold. Unless the child widget manages its own sizing, it will fill the entire remainder of the area inside of the Scaffold by default. In the case of Container, its behavior is to shrink wrap to the size of its child, but if it doesn't have a child, it will fill all available space. So you can just do this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Container(
color: Colors.red,
),
);
}
Depending on the child widget, you may need to manually configure a size, but most layout widgets either you won't need to worry about it or it just takes a bit of configuration. With Column, for example, if you set mainAxisSize: MainAxisSize.max and crossAxisAlignment: CrossAxisAlignment.stretch, then it will also automatically stretch to fill all available space. You can then make use of Expanded to size its children based on available space as well.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(color: Colors.red),
),
Expanded(
child: Container(color: Colors.green),
),
Expanded(
flex: 2,
child: Container(color: Colors.yellow),
),
],
),
);
}
However, on the seldom occasion you need the exact numbers of the space available, you don't usually want to use MediaQuery as that will get you the size of the entire screen, not just the available space, and you usually will have to perform additional calculations to boil it down to the size you are actually looking for (which usually leads to incorrect results, as you have discovered).
Instead, you can use a LayoutBuilder. A word of caution here, you don't want to abuse LayoutBuilder as it adds an additional layout step to its children, so having a lot of them in your code can easily slow down your app.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.biggest.width;
final height = constraints.biggest.height;
return Center(
child: Text('Width: $width, Height: $height'),
);
},
),
);
}
I've recently started using Flutter just for fun, and I'm stuck on adding actual functionality to the code without having everything inside one class.
Essentially, I'm trying to use a FloatingActionButton to increment the value of a Text Widget which stores the value of the user's level as an integer, but I don't want to have the whole app as a StatefulWidget because only the level is going to be updated. When the button is pressed, the value should increment by 1 and then show the new value on the screen.
I have the Level Text Widget inside a StatefulWidget class along with a function to update the level by one and set the state; the MaterialApp inside a StatelessWidget class; and the main body code inside another StatelessWidget class.
If this isn't the best way to do it please do let me know so I can improve for future projects, thanks.
main.dart
import 'package:flutter/material.dart';
main() => runApp(Start());
/// The Material App
class Start extends StatelessWidget{
#override
Widget build(BuildContext context){
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.grey[800],
appBar: AppBar(
title: Text("Home Page"),
backgroundColor: Colors.cyan,
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.orange,
child: Icon(Icons.add, color: Colors.black,),
),
body: HomePage(),
),
);
}
}
/// Main Content for the page (body)
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// removed other children so there's less code to scan through for you :)
Padding(
padding: EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Text that just says "Level"
Text(
"Level",
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.bold,
fontSize: 32,
),
),
// space between text and actual level value
SizedBox(height: 10),
// Create new level widget
Level(),
],
),
),
],
),
);
}
}
/// Updating level using a Stateful Widget
class Level extends StatefulWidget{
#override
State<StatefulWidget> createState(){
return _LevelState();
}
}
class _LevelState extends State<Level>{
int level = 0;
void incrementLevel(){
setState(() {
level += 1;
});
}
#override
Widget build(BuildContext context){
return Text(
"$level",
style: TextStyle(
color: Colors.grey[900],
fontWeight: FontWeight.normal,
fontSize: 28,
),
);
}
}
It actually is a weird way of doing it. However, there is various ways of achieving this
To give an example:
You can use KEYs to remotely redraw the child state
If you want an advanced solution that can assist you in bigger projects. You can use state management tecniques. You can find a lot of tutorials in the internet but these are some of them. BLOC, Provider, InheritedWidget.
Basicaly all of them does the same thing. Lifts up the state data so the place of the redrawn widget on the widget tree will not be important.
I strongly encourage you to watch some tutorials starting with the Provider. I hope this helps
I'm currently designing an app with flutter in dart, and I've been using the Flutter Speed Dial package trying to align it to the bottom-left of the screen using the marginRight and marginBottom values. marginBottom is easy enough, but the problem with marginRight is that depending on the screen size, the same static value might end up putting the widget at the center, all the way left, or even off the screen depending on the size of the screen.
So I called a mediaQuery to get the screen size and base the widget off that, margin just slightly less than the width of whichever screen it is on, to make the value responsive.
However, it says that MediaQuery is called out of context, even though I'm calling it in SpeedDial, which is under FloatingActionButton of Scaffold, set as home of a MaterialApp like so:
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body: Stack(
children: [...]
),
floatingActionButton:
SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
)
);
}
The full error detailed below:
MediaQuery.of() called with a context that does not contain a MediaQuery. No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). This can happed because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
Thanks in advance, this has been rather frustrating :) any help is much appreciated.
To fix your issue just wrap your main Widget as a home of your MaterialApp widget, like this:
main.dart
void main() {
runApp(
MaterialApp(home: YourWidget()),
);
}
And remove the MaterialApp widget here
yourwidget.dart
class YourWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [...]
),
floatingActionButton:
SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
);
}
}
You are losing context as Speeddial is using its own context. to get context back you can use a builder.
Or modify SpeedDial to accept a BuildContext.
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body: Stack(
children: [...]
),
floatingActionButton: Builder(context) {
return SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
}
)
);
}