This article has authorized the public account “code egg”, please indicate the source of reprint
With that Scaffold, we will fill in the remaining holes of AppBar and the usage of other Scaffold parameters. Before starting, we will fill in a simplified version of the Scaffold brain map
The complete version is put on the network disk, small friends download. Complete version of brain map, extraction code: EL9Q, XMIND file extraction code: 1O5D
# # # # AppBar (part2)
In this section, we only focus on the Scaffold AppBar remaining with potholes. The properties of the StatefulWidget will be used later. The difference between StatelessWidget and StatelessWidget can be remembered as the interface that requires data update. Of course, it is not absolute
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> _abs = ['A'.'B'.'S'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true.// The title content is centered
automaticallyImplyLeading: false.// Do not use the default
leading: Icon(Icons.menu, color: Colors.red, size: 30.0), // Left button
flexibleSpace: Image.asset('images/app_bar_hor.jpg', fit: BoxFit.cover), / / the background
title: Text('AppBar Demo', style: TextStyle(color: Colors.red)), // Title content
// A list of action buttons at the end
actions: <Widget>[
PopupMenuButton(
onSelected: (val) => print('Selected item is $val'),
icon: Icon(Icons.more_vert, color: Colors.red),
itemBuilder: (context) =>
List.generate(_abs.length, (index) => PopupMenuItem(value: _abs[index], child: Text(_abs[index])))) ], ), ); }}Copy the code
The final rendering is shown on the left without clicking the right button. Click the right button and the corresponding Mune will pop up
This part of the code is viewedapp_bar_main.dart
file
See the renderer, I believe many friends will joke, “**, the top layer of translucent what, so ugly”, next we will solve the problem, modify the void main method
void main() {
runApp(DemoApp());
// Add the following code to make the status bar transparent
if (Platform.isAndroid) {
varstyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(style); }}Copy the code
Turn it off and run it again, and you can see that the ugly “translucent layer” is gone.
PopupMenuButton
// itemBuilder
typedef PopupMenuItemBuilder<T> = List<PopupMenuEntry<T>> Function(BuildContext context);
// onSelected
typedef PopupMenuItemSelected<T> = void Function(T value);
const PopupMenuButton({
Key key,
@required this.itemBuilder, List
>
this.initialValue, // The initial value is a generic T, that is, the type depends on the value you pass in
this.onSelected, // Select the item callback function that returns T value, such as s if 's' is selected
this.onCanceled, // Without selecting any menu, click on the outside callback to close the mune list
this.tooltip, // Long prompt
this.elevation = 8.0.this.padding = const EdgeInsets.all(8.0),
this.child, // Use to customize the contents of the button
this.icon, // Button icon
this.offset = Offset.zero, // Offset needs to be passed in the x axis, and the y axis Offset will be shifted according to the value passed in
})
Copy the code
####AppBar – bottom
AppBar still has a bottom attribute, because the bottom attribute will be ugly when used with the image background, so we will mention it separately, we will directly modify the original code
/ / here need to use with the introduction of ` SingleTickerProviderStateMixin ` this class
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
List<String> _abs = ['A'.'B'.'S'];
TabController _tabController; // TabBar must pass this parameter
@override
void initState() {
super.initState();
/ / introduce ` SingleTickerProviderStateMixin ` class mainly because _tabController need incoming vsync parameters
_tabController = TabController(length: _abs.length, vsync: this);
}
@override
void dispose() {
// Dispose _tabController before dispose to prevent memory leakage
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
leading: Icon(Icons.menu, color: Colors.red, size: 30.0),
// flexibleSpace: Image.asset('images/app_bar_hor.jpg', fit: BoxFit.cover),
title: Text('AppBar Demo', style: TextStyle(color: Colors.red)),
actions: <Widget>[
PopupMenuButton(
offset: Offset(50.0.100.0),
onSelected: (val) => print('Selected item is $val'),
icon: Icon(Icons.more_vert, color: Colors.red),
itemBuilder: (context) =>
List.generate(_abs.length, (index) => PopupMenuItem(value: _abs[index], child: Text(_abs[index]))))
],
bottom: TabBar(
labelColor: Colors.red, // The selected color
unselectedLabelColor: Colors.white, // Unselected color
controller: _tabController,
isScrollable: false.If the number of tabs exceeds a certain number, the value can be set to true if the row does not fit
indicatorColor: Colors.yellow, // Navigation color
indicatorSize: TabBarIndicatorSize.tab, / / navigation style, another option is TabBarIndicatorSize. The label TAB, navigation and TAB with wide, label, navigation and icon with wide
indicatorWeight: 5.0.// Navigation altitude
tabs: List.generate(_abs.length, (index) => Tab(text: _abs[index], icon: Icon(Icons.android)))), // Navigate to the content list)); }}Copy the code
The final effect is as follows:
####PageView + TabBar
So how to switch the interface through TabBar? Here we need to use PageView, of course there are other parts, such as IndexStack, etc., you can try to use other parts by yourself. Here PageView is associated with TabBar to drive the page switch. PageViede attribute parameter is relatively simple, here will not paste. For the final result, we only show one text so far. Let’s define a general switching interface first
class TabChangePage extends StatelessWidget {
// The parameters to be passed in
final String content;
// TabChangePage(this.content); It is not recommended to write constructors this way
// It is recommended that the key be looked up as a unique value
TabChangePage({Key key, this.content}) : super(key: key);
@override
Widget build(BuildContext context) {
// Display only incoming content
return Container(
alignment: Alignment.center, child: Text(content, style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 30.0))); }}Copy the code
Once a generic interface is defined, it can be passed in and displayed as a child of PageView
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
List<String> _abs = ['A'.'B'.'S'];
TabController _tabController;
// Used for linkage with TabBar
PageController _pageController;
@override
void initState() {
super.initState();
_tabController = TabController(length: _abs.length, vsync: this);
_pageController = PageController(initialPage: 0);
_tabController.addListener(() {
// Check whether TabBar has changed its position, if so, modify the display of PageView
if (_tabController.indexIsChanging) {
// The PageView switch is scrolled by controller
Duration indicates the duration of scrolling, curve indicates the style of scrolling animation,
// The Flutter has defined a number of styles in Curves that can be toggle to see the effect
_pageController.animateToPage(_tabController.index,
duration: Duration(milliseconds: 300), curve: Curves.decelerate); }}); }@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
leading: Icon(Icons.menu, color: Colors.red, size: 30.0),
// flexibleSpace: Image.asset('images/app_bar_hor.jpg', fit: BoxFit.cover),
title: Text('AppBar Demo', style: TextStyle(color: Colors.red)),
actions: <Widget>[
PopupMenuButton(
offset: Offset(50.0.100.0),
onSelected: (val) => print('Selected item is $val'),
icon: Icon(Icons.more_vert, color: Colors.red),
itemBuilder: (context) =>
List.generate(_abs.length, (index) => PopupMenuItem(value: _abs[index], child: Text(_abs[index]))))
],
bottom: TabBar(
labelColor: Colors.red,
unselectedLabelColor: Colors.white,
controller: _tabController,
isScrollable: false,
indicatorColor: Colors.yellow,
indicatorSize: TabBarIndicatorSize.tab,
indicatorWeight: 5.0,
tabs: List.generate(_abs.length, (index) => Tab(text: _abs[index], icon: Icon(Icons.android)))),
),
// Display the content through the body. The body can be passed into any Widget that contains the interface content you need to display
// The body part of the Scaffold was resolved
body: PageView(
controller: _pageController,
children:
_abs.map((str) => TabChangePage(content: str)).toList(), Generate List. Generate List. Generate List
onPageChanged: (position) {
// PageView switch listener, TabBar needs to change after the page of PageView is switched
// Use tabController to change the TabBar display position_tabController.index = position; },),); }}Copy the code
If you slide PageView or click to switch the TabBar position, the content of the Scaffold will change. I’m going to leave you with the drawer and bottomNavigationBar properties, but before we solve those two holes, let’s deal with another problem
That Scaffold allows us to quickly build an interface. However, not all interfaces need the AppBar title. We do not pass in the AppBar property. From the PageView passed in by the body, change the TabChangePage class to have a single TabChangePage. From the TabChangePage class, you can also comment out the Aligment property of the Container so that the contents are displayed in the upper left corner
// _HomePageState
// ..
@override
Widget build(BuildContext context) {
return Scaffold(body: TabChangePage(content: 'Content'));
}
class TabChangePage extends StatelessWidget {
final String content;
TabChangePage({Key key, this.content}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(child: Text(content, style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 30.0))); }}Copy the code
Then run, “**, text blocked by the status bar…” On top of the TabChangePage Container, add a Layer of SafeArea
@override
Widget build(BuildContext context) {
return SafeArea(
child:
Container(child: Text(content, style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 30.0))));
}
Copy the code
Then run again, everything is fine, SafeArea’s purpose can see the source code explanation
/// A widget that insets its child by sufficient padding to avoid intrusions by
/// the operating system.
///
/// For example, this will indent the child by enough to avoid the status bar at
/// the top of the screen.
Copy the code
Leave plenty of room for child widgets and system click invalid areas, such as the status bar and system navigation bar. SafeArea is a great solution to the problem of screen bangs overwriting page content. So far, some of the pits in AppBar have been addressed, and the rest will be addressed
Scaffold – Drawer
The attributes of drawer and endDrawer are the same. In addition to sliding direction, the component of drawer is relatively simple. Just pass in a child. Click “Leading” to slide Drawer out
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true.// automaticallyImplyLeading: false,
// leading: Icon(Icons. Menu, color: Colors. Red, size: 30.0),
// flexibleSpace: Image.asset('images/app_bar_hor.jpg', fit: BoxFit.cover),
title: Text('AppBar Demo', style: TextStyle(color: Colors.red)),
actions: <Widget>[
PopupMenuButton(
offset: Offset(50.0.100.0),
onSelected: (val) => print('Selected item is $val'),
icon: Icon(Icons.more_vert, color: Colors.red),
itemBuilder: (context) =>
List.generate(_abs.length, (index) => PopupMenuItem(value: _abs[index], child: Text(_abs[index]))))
],
bottom: TabBar(
labelColor: Colors.red,
unselectedLabelColor: Colors.white,
controller: _tabController,
isScrollable: false,
indicatorColor: Colors.yellow,
indicatorSize: TabBarIndicatorSize.tab,
indicatorWeight: 5.0,
tabs: List.generate(_abs.length, (index) => Tab(text: _abs[index], icon: Icon(Icons.android)))),
),
// body ....
drawer: Drawer(
// Remember to add 'SafeArea' first to prevent views from popping under the status bar
child: SafeArea(
child: Container(
child: Text('Drawer', style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 30.0))))));// return Scaffold(body: TabChangePage(content: 'Content'));
}
Copy the code
The final renderings are no longer posted. When the gesture slides out from the left or you click on the leading icon, the drawer comes out
AppBar – bottomNavigationBar
BottomNavigationBar can pass in an instance of BottomNavigationBar, which needs to pass in the BottomNavigationBarItem list as items, But in order to do a special combination of bottomNavigationBar and floatingActionButton, instead of using the bottomNavigationBar, let’s call it the BottomAppBar, and just go to the code
@override
Widget build(BuildContext context) {
return Scaffold(
/// same code omitted....
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(icon: Icon(Icons.android, size: 30.0, color: Theme.of(context).primaryColor), onPressed: () {}),
IconButton(icon: Icon(Icons.people, size: 30.0, color: Theme.of(context).primaryColor), onPressed: () {})
],
),
),
floatingActionButton:
FloatingActionButton(onPressed: () => print('Add'), child: Icon(Icons.add, color: Colors.white)),
CenterDocked, endDocked, centerFloat, endFloat, endTop, startTop, miniStartTop
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
Copy the code
The final effect:
Now that we’re talking about StatefulWidget, I’ll mention two simpler widgets that are also basic widgets. CheckBox, CheckboxListTile, Switch, SwitchListTile because it is relatively simple, directly on the code, there are complete annotations
class CheckSwitchDemoPage extends StatefulWidget {
@override
_CheckSwitchDemoPageState createState() => _CheckSwitchDemoPageState();
}
class _CheckSwitchDemoPageState extends State<CheckSwitchDemoPage> {
var _isChecked = false;
var _isTitleChecked = false;
var _isOn = false;
var _isTitleOn = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Check Switch Demo'),
),
body: Column(children: <Widget>[
Row(
children: <Widget>[
Checkbox(
// Whether to enable three states
tristate: true.// Check the checkbox status
value: _isChecked,
// This method is not set and is unavailable
onChanged: (checked) {
// Manage status values
setState(() => _isChecked = checked);
},
// The selected color
activeColor: Colors.pink,
// This value is padded and shrinkWrap,
// PADDED occupies more space than shrinkWrap, I'm sorry I didn't see anything else
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
/// Click no response
Checkbox(value: _isChecked, onChanged: null, tristate: true)
],
),
Row(
children: <Widget>[
Switch(
// The color of the bar when opened
activeTrackColor: Colors.yellow,
// The color of the bar when closed
inactiveTrackColor: Colors.yellow[200].// Set the indicator picture, of course also have color can be set
activeThumbImage: AssetImage('images/ali.jpg'),
inactiveThumbImage: AssetImage('images/ali.jpg'),
// The initial color will appear to be overridden by activeTrackColor
activeColor: Colors.pink,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _isOn,
onChanged: (onState) {
setState(() => _isOn = onState);
}),
/// Click no response
Switch(value: _isOn, onChanged: null)
],
),
CheckboxListTile(
// Describe the options
title: Text('Make this item checked'),
// Secondary description
subtitle: Text('description... description... \ndescription... description... '),
// Secondary is set to the end of the component that is opposite to checkbox
secondary: Image.asset('images/ali.jpg', width: 30.0, height: 30.0),
value: _isTitleChecked,
// If title and subtitle are in a vertically dense list, the most obvious thing is that the parts get smaller
dense: true.// Whether to use the height of 3 rows. If true, subtitle cannot be empty
isThreeLine: true.// Controls whether the checkbox is preceded or followed by the checkbox
controlAffinity: ListTileControlAffinity.leading,
// Whether to apply the theme color to the text or icon
selected: true,
onChanged: (checked) {
setState(() => _isTitleChecked = checked);
},
),
SwitchListTile(
title: Text('Turn On this item'),
subtitle: Text('description... description... \ndescription... description... '),
secondary: Image.asset('images/ali.jpg', width: 30.0, height: 30.0),
isThreeLine: true,
value: _isTitleOn,
selected: true, onChanged: (onState) { setState(() => _isTitleOn = onState); }))); }}Copy the code
This part of the code is viewedcheckbox_swicth_main.dart
file
In the next section, there are still 2 large holes left. We will continue to solve the problem later
The final code address is still needed:
-
The code covered in this article: Demos
-
BLoC mode based on a project of Guo Shen Cool Weather interface to achieve state management: Flutter_weather
-
One course (I bought the code specification at that time and wanted to see it, the code update would be slow, although I wrote the code following the lessons, I still made my own modifications, which made me uncomfortable in many places, and then changed it to my own implementation) : Flutter_shop
If it is helpful to you, please remember to give me a Star. Thank you in advance. Your recognition is the motivation to support me to continue writing