For my new Flutter project I've done some research on layouts, however it still is a bit vague.
Here is the layout I'd like to create:
After some research and watching some videos, it seems this is the MaterialApp code:
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Color(0xFF0E2E40),
title: Text('City Discovery'),
actions: [
Image.asset(
'assets/images/logoicon.png',
width: 35,
)
],
),
body: ListView(
children: [
introSection,
Image.asset(
'assets/images/barman.png',
width: 600,
height: 240,
fit: BoxFit.cover,
),
crimeSection,
mysterySection,
bottomSection,
],
),
),
);
}
But now defining the layout of the different Sections is quite hard.
From what I can tell, there should be:
ListView to make my page scrollable, which it should be
introSection: Container for XYZ with 2 rows for 2 text sections.
Image
crimeSection: Container for XYZ with 3 rows for 2 text sections and button. First text section divided into 2 colums for icon and text
mysterySection: Container for XYZ with 2 rows for 2 text sections. First text section divided into 2 columns for icon and text
bottomSection: Tabs? Although tabs only seem to be part of the appbar, so not sure what the alternative is
The closest example I could find, however changing the code doesn't get me anywhere:
https://github.com/flutter/website/blob/master/examples/layout/lakes/step6/lib/main.dart
For some reason containers can only have 1 child, so that probably means I need a row in my container which then has children which then has 2 childs for both text sections? Incredibly hard to code some simple UI.
If someone can help me with the code for the introSection and the bottomSection, I can probably work out the rest myself.
I recommend to create components folder for reusable widgets, as buttons, logos, text, etc.
If you have a widget that does not used in other components, you can separate it in the same file but use underscore, to make this widget private (It will be visible only inside this file). All that will make your code more readable.
About bottom tabs.
If you want to use it as bottom tabs, you can put it in bottomNavigationBar parameter of Scaffold widget. If you need to use in inside the ListView, it possible but you need to pass the exact height of the tab, because TabView trying to expand as much as possible, same as ListView.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LayoutExample(),
);
}
}
class LayoutExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _BottomNavigationBar(),
body: ListView(
children: <Widget>[
_IntroSection(),
Placeholder(fallbackHeight: 200),
_CrimeSection(),
_MysterySection()
],
),
);
}
}
class _IntroSection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 150,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('Intro text1'),
Text('Intro text2')
],
),
);
}
}
class _CrimeSection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
height: 200,
child: Text('CrimeSection'),
);
}
}
class _MysterySection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
height: 200,
child: Text('MysterySection'),
);
}
}
class _BottomNavigationBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
)
],
),
);
}
}
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'm currently using Scaffold with BottomNavigationBar in a project and need to display a separated widget on top of all that.
Tried to create a Stack on the build return and put this widget and the Scaffold as the children, but the widget is simply not displayed (not showed at all)! There isn't any error messages either.
What could be wrong? Or there is any other approach? (calling AlertDialog won't work for this purpose).
Defining the Stack on the body of the Scaffold didn't work also, as it need a list for the BottomNavigationBar.
Widget build(BuildContext context) {
var Test = Center(
child: SizedBox(width: 100, height: 100,
child: Container(
color: Colors.blue,
child: Text("Test"),),),);
return Stack(
alignment: Alignment.center,
children: [
Test,
Scaffold(
appBar: ..... (rest of the code)
It is because your Scaffold is drawn over your Test widget. Stack takes multiple children and orders them from bottom to top. So the first item is the bottom-most and the last is the top-most. Try the following:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
Scaffold(
appBar: AppBar(title: Text('Hi')),
),
TestWidget(),
],
),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: Colors.blue,
child: Center(
child: Text("Test"),
),
);
}
}
As #HBS mentioned, the problem was caused by the Stack ordering, but the fixing can be achieved just by changing the Widget order:
Widget build(BuildContext context) {
var Test = Center(
child: SizedBox(width: 100, height: 100,
child: Container(
color: Colors.blue,
child: Text("Test"),),),);
return Stack(
alignment: Alignment.center,
children: [
Scaffold(
appBar: ..... (rest of the code),
Test,
],
This is my code to arrange boxes vertically.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
Widget box({width: 100.0, height: 100.0}) => Container(
width: width,
height: height,
color: Colors.grey,
);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}
Since I have a little experience for CSS flexbox, replacing the Column widget with the Row to arrange boxes in row But it can't work. Only black screen appeared. I've already make sure that the code works under the MaterialApp widget.
For learning purpose, I want to keep the code as simple as possible.
What do I need to make the above code work? What is a minimum requirement? Or is there an other widget appropriates for root in such example case?
Thank you for sparing time to read my question!
You need a material app render the colors and stuff
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
);
}
}
MaterialApp widget must be a root widget
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp())); // <- magic
class MyApp extends StatelessWidget {
Widget box({width: 100.0, height: 100.0}) {
return Container(
width: width,
height: height,
color: Colors.grey,
);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}
return MaterialApp(
home: Scaffold(
body: RefreshIndicator(
key: _globalKey,
onRefresh: refresh,
child: Sub(),
),
),
);
there is simple refreshindicator and sub Widget
class Sub extends StatefulWidget {
#override
_SubState createState() => _SubState();
}
class _SubState extends State<Sub> {
#override
Widget build(BuildContext context) {
print('sub');
return Container(
color: Colors.amber,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
height: 300,
color: Colors.blue,
),
Container(
height: 300,
color: Colors.red,
),
Container(
height: 300,
color: Colors.green,
),
],
),
),
);
}
}
Sub Widget print "sub" when it is built
the point is when i touch or just slightly drag page(not enough call refreshindicator) sub widget is rebuilt TWICE. i don't know why and i don't know how can i slove.
(statelesswidget can solve that problem but i need to use statefulwidget)
It depends what you want to achieve without re-building but alternate I used was for flutter charts. Create a object Sub sub1=Sub(); , somewhere outside build() or intiState() , then this widget will not rebuild again and values will be same.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: MaterialButton(
height: 100,
color: Colors.grey,
child: Text("DSG"),
onPressed: () {
print("Drawer sent gesture");
},
),
),
drawer: Container(
height: 300,
decoration: BoxDecoration(
color: Colors.yellow,
),
child: Center(
child: MaterialButton(
height: 100,
color: Colors.grey,
child: Text("DKG"),
onPressed: () {
print("Drawer kept gesture");
},
),
),
),
);
}
}
I know that the IgnorePointer & AbsorbPointer widgets can be used to pass gestures from parent to child & vice versa, resp. but what if I want to pass to a sibling widget, e.g., from drawer to body?
Only gestures belonging to the 1st child of 1 sibling should be passed to the other, e.g., I am asking that "DSG" & "DKG" both be clickable once the drawer is opened, i.e., gestures should pass through the drawer scrim.