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.dartfile

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.dartfile

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:

  1. The code covered in this article: Demos

  2. BLoC mode based on a project of Guo Shen Cool Weather interface to achieve state management: Flutter_weather

  3. 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