I'm new to flutter / dart and am just finding my feet.
Been having a play with a really simple test UI. I'm using the Material App and Scaffold widgets and placing a Column widget in the 'body' of the Scaffold widget. It automatically expands to take up the whole screen when I use the inspect widget tool.
This is the code..
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text('bob'),
],
),
);
}
}
If I then nest a Column widget it doesn't expand to take up the vertical space like its parent did, I can change that and take up the space by using the Expanded widget like this..
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text('bob'),
Expanded(
child: Column(children: [
Text('bob2'),
]),
),
],
),
);
}
}
I'm just trying to understand why the parent 'root' Column takes up all the space it can by default but when I remove the Expanded widget the nested Column doesn't?
Must be something simple but I'm not seeing it when I look through the docs and understand.
Many thanks for any help.
This is a layout constraint applied to the root widget by Flutter.
Related
I am working on flutter project. I want to get content size of horizontal listview. When i click on option in list view , i want to check that option is in proper bound of screen or out of bound. If it is out of bound, then how to move in of bound?
Please suggest and help me to sort out
Thanks in advance
You can use ScrollController to get the size of listView. If it is on Column widget, wrap with Expanded widget to get available space.
class _TDState extends State<XT> {
late final ScrollController controller = ScrollController()
..addListener(() {
print(controller.position.maxScrollExtent);
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
controller: controller,
itemCount: 44,
itemBuilder: (context, index) => Text("item $index"),
))
],
),
);
}
}
This seems like a bug, but I wanted to solicit feedback first. In my simple demo app below, you can see where the SliverChildBuilderDelegate is generating Text widgets as the SliverList. When you scroll up the text widgets overlap with the header. If you wrap the Text widget in a Material widget then that solves the problem (perhaps it's because of Material's clipping capability?). Mixing Material and Cupertino widgets isn't the best, however, particularly if you want to use a CupertinoTheme.
Any suggestions about what's going on and what to do about it is appreciated.
thanks
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Cupertino Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: SafeArea(
child: CustomScrollView(
shrinkWrap: true,
slivers: [
CupertinoSliverNavigationBar(
largeTitle: Text('Cupertino Demo'),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => Material(child: Text('This is list item number $index')),
),
)
],
),
),
);
}
}
Because of the shrinkWrap:true, you can read more here:
https://github.com/flutter/flutter/issues/28197
I don't see why would you need that, so simply just remove and it will work.
'Allo,
My main file is getting up to 1000 lines of code and I can't help but think I could save time by separating the Scaffold into 3 or 4 .dart files. Is this possible?
Between the AppBar and Drawer I'm already up to 500+ lines of code because of all the links and design parameters. I'd like to extricate this code instead of having to scroll through it continually when I'm working on the main body.
Anytime I've tried to take out the drawer and put it in a separate file I get errors everywhere. Problems with 'dynamic' and 'widgets' and return types, etc.
What can I take out the scaffold and reference it to another file?
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],), //end appBar
drawer: new Drawer(......), //end drawer
body: TabBarView(....), //end body
), //end scaffold
I wouldn't mind leaving the main body in this main file but I might also take it out if i had more options. Just want to reduce a 1000+ lines into 2-3 chunks, files of manageable space.
Any ideas?
There is most certainly a way to organize this across different files. In addition to being easier to maintain and test, this may also increase performance if state is involved (because if state changes you have to rebuild the entire tree rather than only rebuilding leaf nodes).
However, this also means that if you have state involved and sprinkled about in your one large build() method then you may have some additional considerations as you organize across files. This is assuming you would create new custom widgets to wrap the various components and you would need to orchestrate the state appropriately.
So with the goal of breaking this build method into different sub Widgets, I recommend you start by breaking it up into functions first:
from:
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],), //end appBar
drawer: new Drawer(......), //end drawer
body: TabBarView(....), //end body
);
}
to:
Widget build(BuildContext context) {
return new Scaffold(
appBar: _appBar(),
drawer: _drawer(),
body: _body(),
);
}
Widget _appBar() {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
Widget _drawer() {
...
}
Widget _body() {
return TabBarView();
}
At this point, you may start to realize what data/state is being passed around as you will have to add parameters to these new helper methods.
If you have a lot of parameter passing (especially on state that changes), you will have other considerations outside the scope of this answer (and we would need to see what state you are actually dealing with).
The next step is to create a new Widget for each of these methods.
From:
Widget _appBar() {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
To:
Widget _appBar(...) {
return MyAppBar(...);
}
class MyAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
}
You can define MyAppBar in it's own file.
You can also bypass the _appBar(...) method and just construct the new widget in the main build() method (assuming you have no other complex setup):
Widget build(BuildContext context) {
return new Scaffold(
appBar: MyAppBar(),
drawer: MyDrawer(),
body: _body(), // you might want to keep the body in the same file
);
}
Easiest way are methods:
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(),
...
);
}
Widget _buildAppBar() {
return AppBar(...);
}
You can also use separate widgets. The widget in the appBar slot must implement PreferredSizeWidget:
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: MyBody(),
);
}
}
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight); // whatever height you want
#override
Widget build(BuildContext context) {
return AppBar();
}
}
class MyBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text('Hello World'),
),
);
}
}
Of course if place them in a different file, you have to import it:
import 'package:myapp/widgets/some_widget.dart';
Is there a way to use setState with StatelessWidget?
I know that I could be used with StatefulWidget and using a State, but I don't know if there's a way to use it with StatelessWidget.
I think that's a direct question and it doesn't need code to be shown.
If you could help me, I will appreciate it.
here is an example of code that makes it possible for a StatelessWidget to update itself, its from an article of Didier Boelens.
https://www.didierboelens.com/2019/09/flutter-internals/
The following useless code makes possible for a StatelessWidget to
update itself (as if it was a StatefulWidget but without using any
setState()), by using the BuildContext …
void main(){
runApp(MaterialApp(home: TestPage(),));
}
class TestPage extends StatelessWidget {
// final because a Widget is immutable (remember?)
final bag = {"first": true};
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: Text('Stateless ??')),
body: Container(
child: Center(
child: GestureDetector(
child: Container(
width: 50.0,`enter code here`
height: 50.0,
color: bag["first"] ? Colors.red : Colors.blue,
),
onTap: (){
bag["first"] = !bag["first"];
//
// This is the trick
//
(context as Element).markNeedsBuild();
}
),
),
),
);
}
}
Between us, when you are invoking the setState() method, the latter
ends up doing the very same thing: _element.markNeedsBuild().
No. That's the whole point of StatelessWidget: It doesn't have a state.
Only StatefulWidget has a state, and therefore only it has a setState.
I have a TextFormField that reloads the current screen when I tap on it to enter text. When I tap on the formfield the software keyboard is displayed briefly before the entire screen reloads and renders all the widgets again. I am running the app on an Android device.
Container(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Your input cannot be empty';
}
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
print('validated');
}
},
child: Text('Save'),
),
),
],
),
),
margin: EdgeInsets.only(top:8.0),
),
The problem is that the controller of the TextFormField is rebuild when you click on the field, and that's the reason of your issue.
So to solve that, did you try to create a Statefull widget and then creating a TextEditingController in the State of this widget and passing it as an argument to the TextFormField ?
I had the same Problem. this was my code
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Model model = Model();
#override
Widget build(BuildContext context) {
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
var mediaWidth = MediaQuery.of(context).size.width / 2.0;
return Scaffold(
...
and I solved this problem by declaring the _formKey outside of build method. and this worked for me.
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Model model = Model();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
var mediaWidth = MediaQuery.of(context).size.width / 2.0;
return Scaffold(
...
hope it will help you
Yes, that happens because when the keyboard appears, the flutter scaffold gets resize to the current available screen size. So, we can easily handle this by preventing the scaffold size change. I suggest to set scaffold resizeToAvoidBottomInset property false. If it's true the body and the scaffolds floating widgets should size themselves to avoid the onscreen keyboard whose height is defined by the ambient MediaQuery's, MediaQueryData,viewInsets bottom property.
Solution:
resizeToAvoidBottomInset: false,
Complete example:
#override
Widget build(BuildContext context) {
setDisplayData();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: getAppBar(),
body: OrientationBuilder(
builder: (context, orientation) {
return orientation == Orientation.portrait
? _buildVerticalLayout()
: _buildHorizontalLayout();
},
),
);
Check if you are using MediaQueries wrongly in your project, I had similar issue and it stopped when I changed the MediaQuery
in my case:
Size _size = MediaQuery.of(context).size;
removing this piece of code fixed my app.
When TextFormField focused the size of screen will changed because of the appearance of keyboard, that cause rebuild of state, you cant prevent re-build of state.
Instead of trying prevent re-build state, you need to solve problems which happen when state do re-build, one of common problem is declaration and initialization variables inside build(BuildContext context){ ... }' function.
The main problem, when you need to get some data related of context (like size of screen), in this case I prefer to pass this value from parent Widget...
For example this code will cause problem when re-build state:
#override
Widget build(BuildContext context) {
double? _screenHeight = MediaQuery.of(context).size.height;
return Container();
}
To solve problem get _screenHeight from parent, to know how to do that look at https://stackoverflow.com/a/50289032/2877427