Basic notes of Flutter

Common properties and methods in List:

/*
List Common attributes and methods:

   Common attributes:
       length          length
       reversed        Flip
       isEmpty         Is it empty
       isNotEmpty      Is it not empty
   Common methods:  
       add         increase
       addAll      Splice array
       indexOf     Find the specific value passed in
       remove      Delete incoming specific value
       removeAt    Delete incoming index value
       fillRange   modify   
       insert(index,value);            Insert at specified location    
       insertAll(index,list)           Insert List at the specified location
       toList()    Convert other types to List  
       join()      List Convert to string
       split()     Convert string to List
       forEach   
       map
       where
       any
       every

*/


void main(){

 // List myList = ['Banana', 'Apple', 'watermelon'];
 // print(myList[1]);


 // var list=new List();
 // list.add('111');
 // list.add('222');
 // print(list);


//Properties in List:
   // List myList = ['Banana', 'Apple', 'watermelon'];
   // print(myList.length);
   // print(myList.isEmpty);
   // print(myList.isNotEmpty);
   // print(myList.reversed);  // Sort the list in reverse order
   // var newMyList=myList.reversed.toList();
   // print(newMyList);

//Methods in List:


   // List myList = ['Banana', 'Apple', 'watermelon'];
   //myList.add('peach ')// Add data add one

   // myList.addAll(['peach', 'grape'])// Splice array

   // print(myList);

   //print(myList.indexOf('apple x fruit ')// indexOf finds data, returns - 1 if not found, returns the index value


   // myList.remove('watermelon ');

   // myList.removeAt(1);

   // print(myList);
 



   // List myList = ['Banana', 'Apple', 'watermelon'];

   // myList.fillRange(1, 2,'aaa');  // modify

   //  myList.fillRange(1, 3,'aaa');  


   // myList.insert(1,'aaa');      // Insert a

   // myList.insertAll(1, ['aaa','bbb']);  // Insert multiple

   // print(myList);




   // List myList = ['Banana', 'Apple', 'watermelon'];

   // var str=myList. join('-');   // Convert list to string

   // print(str);

   // print(str is String);  //true


   var str='Banana-Apple-watermelon';

   var list=str.split('-');

   print(list);

   print(list is List);

}

Set

//Set 

//Its main function is to remove the duplicate contents of the array

//Set is a set that has no order and cannot be repeated, so it cannot get the value through the index

void main(){

  
  // var s=new Set();
  // s.add('banana ');
  // s.add('apple ');
  // s.add('apple ');

  // print(s);   // {banana, apple}

  // print(s.toList()); 


  List myList=['Banana','Apple','watermelon','Banana','Apple','Banana','Apple'];

  var s=new Set();

  s.addAll(myList);

  print(s);

  print(s.toList());


  
}

Map

/*
  Maps are unordered key value pairs:

    Common attributes:
        keys            Get all key values
        values          Get all value values
        isEmpty         Is it empty
        isNotEmpty      Is it not empty
    Common methods:
        remove(key)     Delete the data of the specified key
        addAll({...})   Merge mapping adds attributes to the map
        containsValue   View the value in the map and return true/false
        forEach   
        map
        where
        any
        every


*/

void main(){

 
  // Map person={
  //   "name": "Zhang San",
  //   "age":20
  // };


  // var m=new Map();
  // m["name"] = "Li Si";
  
  // print(person);
  // print(m);

//Common attributes:

    // Map person={
    //   "name": "Zhang San",
    //   "age":20,
    //   "sex": "male"
    // };

    // print(person.keys.toList());
    // print(person.values.toList());
    // print(person.isEmpty);
    // print(person.isNotEmpty);


//Common methods:
    Map person={
      "name":"Zhang San",
      "age":20,
      "sex":"male"
    };

    // person.addAll({
    //   "work": ['code tapping', 'delivery'],
    //   "height":160
    // });

    // print(person);



    // person.remove("sex");
    // print(person);



    print(person.containsValue('Zhang San'));
}

forEach,map, where,any,every

/*
        forEach     
        map         
        where       
        any
        every
*/
void main(){


      //  List myList = ['Banana', 'Apple', 'watermelon'];

      // for(var i=0;i<myList.length;i++){
      //   print(myList[i]);
      // }


      // for(var item in myList){
      //   print(item);
      // }


      // myList.forEach((value){
      //     print("$value");
      // });





      // List myList=[1,3,4];

      // List newList=new List();

      // for(var i=0;i<myList.length;i++){

      //   newList.add(myList[i]*2);
      // }
      // print(newList);





      // List myList=[1,3,4];      
      // var newList=myList.map((value){
      //     return value*2;
      // });
      //  print(newList.toList());





      // List myList=[1,3,4,5,7,8,9];

      // var newList=myList.where((value){
      //     return value>5;
      // });
      // print(newList.toList());



      // List myList=[1,3,4,5,7,8,9];

      // var f=myList.any((value) {/ / returns true as long as there are qualified ones in the collection

      //     return value>5;
      // });
      // print(f);



      // List myList=[1,3,4,5,7,8,9];

      // var f=myList.every((value) {/ / returns true if each condition is met; otherwise, returns false

      //     return value>5;
      // });
      // print(f);






      // set

      // var s=new Set();

      // s.addAll([1,222,333]);

      // s.forEach((value)=>print(value));



      //map

       Map person={
          "name":"Zhang San",
          "age":20
        };

        person.forEach((key,value){            
            print("$key---$value");
        });

}

Extensions abstract classes and implements

/*
Dart Abstract class in: Dart abstract class is mainly used to define standards. Subclasses can inherit abstract classes or implement abstract class interfaces.


  1,Abstract classes are defined by the abstract keyword

  2,Dart Abstract methods in cannot be declared with abstract. Methods without method body in Dart are called abstract methods.

  3,If a subclass inherits an abstract class, it must implement the abstract methods inside

  4,If you implement an abstract class as an interface, you must implement all the properties and methods defined in the abstract class.

  5,Abstract classes cannot be instantiated, only subclasses that inherit them can

extends Differences between abstract classes and implements:

  1,If we want to reuse the methods in the abstract class and use the abstract method to restrict our own class, we will inherit the abstract class with extensions

  2,If we only take abstract classes as the standard, we will use implements to implement abstract classes

*/

Flutter environment construction

Install the latest Xcode

Download Android studio

https://developer.android.google.cn/studio

Download the fluent SDK

https://flutter.dev/docs/development/tools/sdk/releases?tab=macos

Decompress the downloaded fluent Sdk to the directory where you want to install Sdk, such as

/Users/cc/flutter

Configure the bin directory of the Flutter installation directory to the environment variable, and then configure the Flutter domestic image to the environment variable

 vim ~/.zshrc
export PATH=/Users/cc/flutter/bin:$PATH
export ANDROID_HOME="/Users/cc/Library/Android/sdk"
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
source ~/.zshrc

flutter -h if some commands can be issued, the flutter sdk configuration is successful.

Note that if you enter "flutter -h" to tell you that flutter is not a built-in command after configuration, the sdk may not be configured successfully, or the sdk may not be fully downloaded during download

Run the fluent doctor command to detect the environment

Entry file and entry method

There is a main in the lib directory of each fluent project Dart is the entry file of fluent

main. In dart

void main() {
  runApp(MyApp());
}
//It can also be abbreviated
void main() => runApp(MyApp());

The main method is the entry method of dart. The runApp method is the entry method of the shuttle. MyApp is a custom component

Use of the first Demo Center component

import 'package:flutter/material.dart';

void main() {
  runApp(Center(
    child: Text(
      "I am a text content",
      textDirection: TextDirection.ltr,
    ),
  ));
}

Separate the content into a component

The custom component in fluent is actually a class, which needs to inherit StatelessWidget/StatefulWidget

In the early stage, we all inherited StatelessWidget. Later, I'll tell you about the use of StatefulWidget.

Stateless widget is a stateless component with immutable state
Stateful widget is a stateful component, and the state held may change in the widget life cycle

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "I am a text content",
        textDirection: TextDirection.ltr,
      ),
    );
  }
}

Add some decoration to the Text component

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "I am a text content",
        textDirection: TextDirection.ltr,
        style: TextStyle(
            fontSize: 40.0,
            fontWeight: FontWeight.bold,
            // color: Colors.yellow
          color: Color.fromRGBO(255, 222, 222, 0.5)
        ),
      ),
    );
  }
}

Decorate the App with two components: MaterialApp and Scaffold

1,MaterialApp

MaterialApp is a convenient widget, which encapsulates some widgets required by the application to realize Material Design. General use of top-level widgets.

Common attributes:

Home (home)

Title

Color

Theme

Routes

...

2,Scaffold

Scaffold is the basic implementation of Material Design layout structure. This class provides API s for displaying drawer s, snapbars, and bottom sheet s.

Scaffold has the following main properties:

AppBar - an AppBar displayed at the top of the interface.

body - the main content displayed in the current interface Widget.

Drawer - drawer menu control.
...

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "I'm a title",
      home: Scaffold(
        appBar: AppBar(
          title: Text("Hello Flutter"),
          elevation: 30.0, Set the title shadow to 0 if not required.0
        ),
        body: HomeContent(),
      ),
      theme: ThemeData(
          //Set theme color
          primarySwatch: Colors.yellow),
    );
  }
}

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "I am a text content",
        textDirection: TextDirection.ltr,
        style: TextStyle(
            fontSize: 40.0,
            fontWeight: FontWeight.bold,
            // color: Colors.yellow
            color: Color.fromRGBO(255, 222, 222, 0.5)
        ),
      ),
    );
  }
}

Text component

namefunction
textAlignText alignment (center, left, right, justfy)
textDirectionText direction (ltr from left to right, rtl from right to left)
overflowHow to deal with the text after it exceeds the screen (clip clipping, fade fade, ellipsis)
textScaleFactorFont display magnification
maxLinesMaximum lines of text display
styleFont style settings

Here are the parameters of TextStyle:

namefunction
decorationText decorative line (none, lineThrough, strikeout, overline, underline)
decorationColorText decorative line color
decorationStyleText decorative line style ([dashed,dotted] dotted line, double two lines, solid one solid line, wavy wavy line)
wordSpacingWord gaps (negative values make words more compact)
letterSpacingLetter gap (negative values make letters more compact)
fontStyleText style (italic, normal, normal)
fontSizeText size
colorText color
fontWeightFont thickness (bold bold, normal)

More parameters: https://docs.flutter.io/flutter/painting/TextStyle-class.html

Container component

namefunction
alignmenttopCenter: align top center
Left: top left
topRight: align top right
Center: horizontal vertical center alignment
centerLeft: vertically centered and horizontally aligned to the left
centerRight: vertically centered and horizontally right aligned
Align bottomCenter bottom center
bottomLeft: align bottom to left
bottomRight: align bottom right
decorationdecoration: BoxDecoration(
color: Colors.blue,
border: Border.all(
color: Colors.red,
width: 2.0,
),
borderRadius:
BorderRadius.all(
Radius.circular(8.0)
)
)
marginThe margin property indicates the distance between the Container and other external components.
EdgeInsets.all(20.0),
paddingpadding is the inner margin of the Container, which refers to the distance between the edge of the Container and the Child
padding: EdgeInsets.all(10.0)
transformMake it easy for the Container to rotate and so on
transform: Matrix4.rotationZ(0.2)
heightContainer height
widthContainer width
childContainer child element

More parameters: https://api.flutter.dev/flutter/widgets/Container-class.html

Picture component

The Image component is a component that displays images. The Image component has many constructors. Here we will only tell you two

Image.asset local picture

Image.network remote picture

Common properties of Image component:

nametypeexplain
alignmentalignmentPicture alignment
color and colorBlendModeSet the background color of the picture, which is usually used together with colorBlendMode, so that the picture color can be mixed with the background color. The above picture is a mixture of colors, green background and red
fitBoxFitThe fit attribute is used to control the stretching and squeezing of the picture, which is based on the parent container.
BoxFit.fill: the full image is displayed. The image will be stretched and filled with the parent container.
BoxFit.contain: full picture display, showing the original scale, there may be gaps.
BoxFit.cover: the display may stretch, cut and fill (the picture should fill the whole container without deformation).
BoxFit.fitWidth: width full (horizontal full), indicating possible stretching and cutting.
BoxFit.fitHeight: height filling (vertical filling), indicating possible stretching and cutting.
BoxFit.scaleDown: the effect is similar to that of contain, but this property does not allow the display to exceed the size of the source image. It can be small or large.
repeatTileImageRepeat.repeat: repeat horizontally and vertically until the whole canvas is covered.
ImageRepeat.repeatX: horizontal repetition, vertical non repetition.
ImageRepeat. Repeat: repeat vertically and not horizontally.
widthThe width is generally combined with ClipOval to see the effect
heightThe effect can only be seen when the height is generally combined with ClipOval

More attribute references: https://api.flutter.dev/flutter/widgets/Image-class.html

return Center(
      child: Container(
        child: Image.network(
          "http://pic.baike.soso.com/p/20130828/20130828161137-1346445960.jpg",
          alignment: Alignment.topLeft,
          color: Colors.red,
          colorBlendMode: BlendMode.colorDodge,
          // repeat: ImageRepeat.repeatX,
          fit: BoxFit.cover,
        ),
        width: 300.0,
        height: 400.0,
        decoration: BoxDecoration(
            color: Colors.yellow
        ),
      ),
    );

Import local pictures

emmm... Forget it

Clip rect, ClipRRect, ClipOval, ClipPath, CustomClipper

widget function
ClipRect cuts the child to the given rectangular size
Clipprrect cuts the child to a rounded rectangle
ClipOval if the child is a square, the clipping is followed by a circle; if the child is a rectangle, the clipping is followed by an ellipse
ClipPath cuts the child according to the given path
CustomClipper is not a widget, but we can draw any shape we want with CustomClipper

Rounded corners and circular pictures

Implement fillet image

return Center(
      child: Container(
        width: 300.0,
        height: 300.0,
        decoration: BoxDecoration(
            color: Colors.yellow,
            borderRadius: BorderRadius.circular(150),
            image: DecorationImage(
                image: NetworkImage(
                  "http://pic.baike.soso.com/p/20130828/20130828161137-1346445960.jpg",
                ),
                fit: BoxFit.cover
            )
        ),
      ),
    );

Realize circular picture

return Center(
      child: Container(
        child: ClipOval(
          child: Image.network(
            "https://www.itying.com/images/201905/thumb_img/1101_thumb_G_1557845381862.jpg",
            width: 150.0,
            height: 150.0,
          ),
        ),
      ),
    );

Round head

ClipOval

new ClipOval(
    child: new Image.asset(Utils.getImgPath('ali_connors')),
  )

② CircleAvatar

new CircleAvatar(
    radius: 36.0,
    backgroundImage: AssetImage(
      Utils.getImgPath('ali_connors'),
    ),
  )

③ BoxDecoration BoxShape.circle

 new Container(
    width: 72.0,
    height: 72.0,
    decoration: BoxDecoration(
      shape: BoxShape.circle,
      image: DecorationImage(
        image: AssetImage(
          Utils.getImgPath('ali_connors'),
        ),
      ),
    ),
  )

Rounded head

① ClipRRect

new ClipRRect(
    borderRadius: BorderRadius.circular(6.0),
    child: new Image.asset(Utils.getImgPath('ali_connors')),
  )

② BoxDecoration BoxShape.rectangle

new Container(
    width: 88.0,
    height: 88.0,
    decoration: BoxDecoration(
      shape: BoxShape.rectangle,
      borderRadius: BorderRadius.circular(6.0),
      image: DecorationImage(
        image: AssetImage(
          Utils.getImgPath('ali_connors'),
        ),
      ),
    ),

List component overview

List layout is the most commonly used layout method in our project development. In fluent, we can define list items through ListView, which supports vertical and horizontal display. You can control the display direction of the list through an attribute. The list has the following categories:

**1. Vertical list (the width is automatically expanded, and the setting width is invalid) * * it can be controlled in the outer package Container

2. Vertical text list

**3. Horizontal list (the height is automatically expanded, and the setting height is invalid) * * can be controlled in the outer package Container

4. Dynamic list

5. Matrix list (grid layout)

List parameters

nametypeexplain
scrollDirectionAxisAxis.horizontal list
Axis.vertical list
paddingEdgeInsetsGeometrypadding
resolveboolReverse ordering of components
childrenListList element

Basic list

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ListView(
        children: <Widget>[
          ListTile(
            leading: Icon(Icons.phone),
            title: Text("this is list", style: TextStyle(fontSize: 28.0)),
            subtitle: Text('this is list this is list'),
          ),
          ListTile(
            title: Text("this is list"),
            subtitle: Text('this is list this is list'),
            trailing: Icon(Icons.phone),
          ),
          ListTile(
            title: Text("this is list"),
            subtitle: Text('this is list this is list'),
          ),
          ListTile(
            title: Text("this is list"),
            subtitle: Text('this is list this is list'),
          ),
          ListTile(
            title: Text("this is list"),
            subtitle: Text('this is list this is list'),
          )
        ],
      ),
    );
  }
}

Horizontal list

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200.0,
      margin: EdgeInsets.all(5),
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          Container(
            width: 180.0,
            color: Colors.lightBlue,
          ),
          Container(
            width: 180,
            color: Colors.amber,
            child: ListView(
              children: <Widget>[
                Image.network(
                    "https://resources.ninghao.org/images/childhood-in-a-picture.jpg"),
                SizedBox(height: 16.0),
                Text("This is a text message",
                    textAlign: TextAlign.center,
                    style: TextStyle(fontSize: 16.0)),
              ],
            ),
          )
        ],
      ),
    );
  }
}

Dynamic list (dynamic cyclic data)

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: HomeContent(),
      ),
    );
  }
}

class HomeContent extends StatelessWidget {
  List list = List();

  HomeContent() {
    for (int i = 0; i < 20; i++) {
      list.add("This is the second $i Data bar");
    }
    print(list);
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        itemCount: this.list.length,
        itemBuilder: (context, index) {
          // print(context);
          return ListTile(
            leading: Icon(Icons.phone),
            title: Text("${list[index]}"),
          );
        });
  }
}

Common parameters of GridView component

When the amount of data is large, it is clear to arrange it in a matrix. At this point, we can use the grid list component GridView to realize the layout.

There are many ways for GridView to create grid lists. We will mainly introduce two ways below.

1. You can use GridView Count to realize grid layout

2. Through GridView Implementation of grid layout with builder

Common attributes:

nametypeexplain
scrollDirectionAxisRolling method
paddingEdgeInsetsGeometrypadding
resolveboolReverse ordering of components
crossAxisSpacingdoubleSpacing between horizontal subwidgets
mainAxisSpacingdoubleSpacing between vertical subwidgets
crossAxisCountintNumber of widgets in a row
childAspectRatiodoubleWidth height ratio of child Widget
children[ ]
gridDelegateSliverGridDelegateWithFix
Edcrossaxiscount (common)
SliverGridDelegateWithMax
CrossAxisExtent
Control layout is mainly used in GridView Inside the builder

GridView.count to realize grid layout

import 'package:cc/res/listData.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutContent(),
      ),
    );
  }
}

class LayoutContent extends StatelessWidget {
  List<Widget> _getListData() {
    var tempList = listData.map((value) {
      return Container(
        child: Column(
          children: <Widget>[
            Image.network(value["imageUrl"]),
            SizedBox(height: 12),
            Text(value["title"],
                textAlign: TextAlign.center, style: TextStyle(fontSize: 20))
          ],
        ),
        decoration: BoxDecoration(
            border: Border.all(
                color: Color.fromRGBO(230, 230, 230, 0.9), width: 1.0)),
      );
    });
    // ('124124','124214')
    return tempList.toList();
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 2,
      crossAxisSpacing: 20,
      mainAxisSpacing: 20,
//       childAspectRatio:0.7,
      children: this._getListData(),
    );
  }
}

GridView. Implementation of grid layout with builder

import 'package:cc/res/listData.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutContent(),
      ),
    );
  }
}

class LayoutContent extends StatelessWidget {
  Widget _getListData(context, index) {
    return Container(
      child: Column(
        children: <Widget>[
          Image.network(listData[index]["imageUrl"]),
          SizedBox(height: 12),
          Text(listData[index]["title"],
              textAlign: TextAlign.center, style: TextStyle(fontSize: 20))
        ],
      ),
      decoration: BoxDecoration(
          border: Border.all(
              color: Color.fromRGBO(230, 230, 230, 0.9), width: 1.0)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      itemCount: listData.length,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          //Number of horizontal axis elements
          crossAxisCount: 2,
          //Longitudinal axis spacing
          mainAxisSpacing: 20.0,
          //Horizontal axis spacing
          crossAxisSpacing: 10.0,
          //Sub assembly width height length ratio
          childAspectRatio: 1.0),
      itemBuilder: this._getListData,
    );
  }
}

Padding component

Common layout tags in html have padding attribute, but many widgets in fluent do not have padding attribute. At this time, we can use the padding component to deal with the direct spacing between the container and the child elements.

attributeexplain
paddingPadding value, EdgeInsetss sets the value of padding
childSubcomponents
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.fromLTRB(0, 0, 10, 0),
      child: GridView.count(
        crossAxisCount: 2,
        childAspectRatio: 1.5,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/1.png",
                fit: BoxFit.cover),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/2.png",
                fit: BoxFit.cover),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/3.png",
                fit: BoxFit.cover),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/4.png",
                fit: BoxFit.cover),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/5.png",
                fit: BoxFit.cover),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
            child: Image.network("https://www.itying.com/images/flutter/6.png",
                fit: BoxFit.cover),
          ),
        ],
      ),
    );
  }
}

Row horizontal layout component

attributeexplain
mainAxisAlignmentSorting method of spindle
crossAxisAlignmentSorting method of secondary axis
childrenComponent child element
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutDemo(),
      ),
    );
  }
}

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 700,
      width: 500,
      color: Colors.black26,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
//        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          IconContainer(Icons.home, color: Colors.red),
          IconContainer(Icons.home, color: Colors.blue),
          IconContainer(Icons.home, color: Colors.orange),
        ],
      ),
    );
  }
}

class IconContainer extends StatelessWidget {
  double size;
  IconData icon;
  Color color;

  IconContainer(this.icon, {this.size, this.color = Colors.blue}) {
    this.size = 32.0;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: this.size + 60,
      height: this.size + 60,
      color: this.color,
      child: Center(
          child: Icon(
        this.icon,
        color: Colors.white,
        size: this.size,
      )),
    );
  }
}

Column vertical layout component

attributeexplain
mainAxisAlignmentSorting method of spindle
crossAxisAlignmentSorting method of secondary axis
childrenComponent child element
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutDemo(),
      ),
    );
  }
}

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 700,
      width: 500,
      color: Colors.black26,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
//        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          IconContainer(Icons.home, color: Colors.red),
          IconContainer(Icons.home, color: Colors.blue),
          IconContainer(Icons.home, color: Colors.orange),
        ],
      ),
    );
  }
}

class IconContainer extends StatelessWidget {
  double size;
  IconData icon;
  Color color;

  IconContainer(this.icon, {this.size, this.color = Colors.blue}) {
    this.size = 32.0;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: this.size + 60,
      height: this.size + 60,
      color: this.color,
      child: Center(
          child: Icon(
        this.icon,
        color: Colors.white,
        size: this.size,
      )),
    );
  }
}

Expanded is similar to the Flex layout in the Web

Expanded can be used in Row and Column layouts

attributeexplain
flexThe proportion of the entire parent Row /Column of the element station
childChild element
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutDemo(),
      ),
    );
  }
}

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10),
      child: Row(
        // crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.center,
        // crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Expanded(flex: 2, child: IconContainer(Icons.home)),
          SizedBox(width: 10),
          Expanded(flex: 3, child: IconContainer(Icons.search)),
          // SizedBox(width: 10),
          // Expanded(child: IconContainer(Icons.send))
        ],
      ),
    );
  }
}

class IconContainer extends StatelessWidget {
  double size;
  IconData icon;

  IconContainer(this.icon, {this.size}) {
    this.size = 32.0;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100.0,
      height: 100.0,
      color: Colors.blue,
      child:
          Center(child: Icon(this.icon, color: Colors.white, size: this.size)),
    );
  }
}

Stack assembly

Stack means heap. We can use stack or stack combined with Align or stack combined with position d to realize the positioning and layout of the page

attributeexplain
alignmentConfigure the display position of all child elements
childrenSubcomponents
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Stack(
        alignment: Alignment.topLeft,
        children: <Widget>[              
          Container(
            height: 400,
            width: 300,
            color: Colors.red,
          ),
          Text('I am a text',style: TextStyle(
            fontSize: 40,
            color: Colors.white
          ))           
        ],
      ),
    );
  }
}
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Stack(
        alignment: Alignment(1,0.3),
        children: <Widget>[              
          Container(
            height: 400,
            width: 300,
            color: Colors.red,
          ),
          Text('I am a text',style: TextStyle(
            fontSize: 20,
            color: Colors.white
          ))           
        ],
      ),
    );
  }
}

Stack Align

Stack component combined with Align component can control the display position of each child element

attributeexplain
alignmentConfigure the display position of all child elements
childSubcomponents
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child:  Container(
            height: 400,
            width: 300,
            color: Colors.red,
            child: Stack(
              // alignment: Alignment.center,
              children: <Widget>[
                Align(
                  alignment: Alignment(1,-0.2),
                  child: Icon(Icons.home,size: 40,color: Colors.white),
                ),
                Align(
                  alignment: Alignment.center,
                  child: Icon(Icons.search,size: 30,color: Colors.white),
                ),
                Align(
                  alignment: Alignment.bottomRight,
                  child: Icon(Icons.settings_applications,size: 30,color: Colors.white),
                )
              ],
            ),
      ),
    );
  }
}

Stack Positioned

The Stack component combined with the Positioned component can also control the display position of each child element

attributeexplain
topDistance from child element to top
bottomDistance of child element from bottom
leftDistance from child element to left
rightDistance from child element to right
childSubcomponents
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child:  Container(
            height: 400,
            width: 300,
            color: Colors.red,
            child: Stack(
              // alignment: Alignment.center,
              children: <Widget>[
                Positioned(
                //  left: 10,
                  child: Icon(Icons.home,size: 40,color: Colors.white),
                ),
                Positioned(
                 bottom: 0,
                 left: 100,
                  child: Icon(Icons.search,size: 30,color: Colors.white),
                ),
                Positioned(
                  right: 0,
                  child: Icon(Icons.settings_applications,size: 30,color: Colors.white),
                )
              ],
            ),
      ),
    );
  }
}

AspectRatio component

AspectRatio is used to adjust the aspect ratio of the child element according to the setting.

AspectRatio will first expand as much as possible within the scope allowed by the layout constraints. The height of the widget is determined by the width and ratio, similar to the contain in BoxFit. It will occupy the area as much as possible according to a fixed ratio.

If a feasible size cannot be found after all constraints are met, AspectRatio will eventually give priority to the layout constraints and ignore the set ratio.

attributeexplain
aspectRatioThe aspect ratio may not be laid out according to this value in the end. It depends on the comprehensive factors. Whether the outer layer is allowed to be laid out according to this ratio is only a reference value
childSubcomponents
return Center(
      child: Container(
        width: 200,
        child: AspectRatio(
          aspectRatio: 2.0 / 1.0,
          child: Container(
            color: Colors.red,
          ),
        ),
      ),
    );

Card assembly

Card is a card component block. The content can be composed of most types of widgets. Card has rounded corners and shadows, which makes it look three-dimensional.

attributeexplain
marginMargin
childSubcomponents
ShapeThe shadow effect of Card. The default shadow effect is the rounded rectangular edge.
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        Card(
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              ListTile(
                title: Text(
                  "Zhang San",
                  style: TextStyle(fontSize: 28),
                ),
                subtitle: Text("Senior Software Engineer"),
              ),
              Divider(),
              ListTile(
                title: Text("Tel: 123123123"),
              ),
              ListTile(
                title: Text("Address: Haidian District, Beijing"),
              )
            ],
          ),
        ),
        Card(
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              ListTile(
                title: Text(
                  "Li Si",
                  style: TextStyle(fontSize: 28),
                ),
                subtitle: Text("Senior Software Engineer"),
              ),
              Divider(),
              ListTile(
                title: Text("Tel: 123123123"),
              ),
              ListTile(
                title: Text("Address: Haidian District, Beijing"),
              )
            ],
          ),
        ),
      ],
    );
  }
}

Card component implements a graphic list layout

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
        children: listData.map((value) {
      return Card(
        margin: EdgeInsets.all(10),
        child: Column(
          children: <Widget>[
            AspectRatio(
              aspectRatio: 16 / 9,
              child: Image.network(value["imageUrl"], fit: BoxFit.cover),
            ),
            ListTile(
              leading: CircleAvatar(
                backgroundImage: NetworkImage(value["imageUrl"]),
              ),
              title: Text(value["description"]),
              subtitle: Text(
                value["description"],
                overflow: TextOverflow.ellipsis,
              ),
            )
          ],
        ),
      );
    }).toList());
  }
}
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: listData.length,
      itemBuilder: (context,index){
        return Card(
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              AspectRatio(
                aspectRatio: 16 / 9,
                child: Image.network(listData[index]["imageUrl"], fit: BoxFit.cover),
              ),
              ListTile(
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(listData[index]["imageUrl"]),
                ),
                title: Text(listData[index]["description"]),
                subtitle: Text(
                  listData[index]["description"],
                  overflow: TextOverflow.ellipsis,
                ),
              )
            ],
          ),
        );
      },
    );
  }
}

RaisedButton defines a button

A button is defined in the shutter through RaisedButton. There are many parameters in RaisedButton. We just use them simply.

return RaisedButton(
        child: Text("Flutter"),
        textColor: Theme.of(context).accentColor,
    onPressed: (){
          
    });

Wrap component

Wrap can realize flow layout. The performance of single Row wrap is almost the same as that of Row, and that of single Column wrap is almost the same as that of Row. However, both Row and Column are single Row and single Column. Wrap breaks through this limitation. When there is insufficient space on mainAxis, it will expand the display to crossAxis.

attributeexplain
directionSpindle direction, default horizontal
alignmentSpindle alignment
spacingSpacing in spindle direction
textDirectionText direction
verticalDirectionThe placement order of children is defined. The default is down. See the introduction to Flex related attributes.
runAlignmentAlignment of run. Run can be understood as a new row or column. If the layout is horizontal, run can be understood as a new row
runSpacingrun spacing
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: LayoutDemo(),
      ),
    );
  }
}

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 10,
      runSpacing: 10,
      alignment: WrapAlignment.spaceEvenly,
      children: <Widget>[
        MyButton("Episode 1"),
        MyButton("Episode 2"),
        MyButton("Episode 3"),
        MyButton("Episode 4"),
        MyButton("Episode 5"),
        MyButton("Episode 6 Episode 6"),
        MyButton("Episode 7"),
        MyButton("Episode 8 Episode 6"),
        MyButton("Episode 9"),
        MyButton("Episode 10"),
        MyButton("Episode 11"),
      ],
    );
  }
}

class MyButton extends StatelessWidget {
  final String text;

  const MyButton(this.text, {Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
        child: Text(this.text),
        textColor: Theme.of(context).accentColor,
        onPressed: () {});
  }
}

Custom stateful component

In fluent, a custom component is actually a class that needs to inherit StatelessWidget/StatefulWidget.

Stateless widget is a stateless component with immutable state
Stateful widget is a stateful component, and the state held may change in the widget life cycle. Generally speaking: if we want to change the data in the page, we need to use stateful widget at this time

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Hello Flutter")),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          Chip(label: Text("${this.count}")),
          RaisedButton(
              child: Text("increase"),
              onPressed: () {
//                print(this.count);
                setState(() {
                  this.count++;
                });
              })
        ],
      ),
    );
  }
}

BottomNavigationBar component

bottomNavigationBar is the bottom navigation bar, which allows us to define the bottom Tab switch. bottomNavigationBar is the parameter of Scaffold component.

BottomNavigationBar common properties

Attribute nameexplain
itemsList bottom navigation bar button collection
iconSizeicon
currentIndexWhich is selected by default
fixedColorSelected color
typeBottomNavigationBarType.fixed
BottomNavigationBarType.shifting
(four bottom navigation display errors solved above)
class Tabs extends StatefulWidget {
  Tabs({Key key}) : super(key: key);

  _TabsState createState() => _TabsState();
}

class _TabsState extends State<Tabs> {

  int _currentIndex=0;
  List _pageList=[
    HomePage(),
    CategoryPage(),
    SettingPage(),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Flutter Demo"),
        ),
        body: this._pageList[this._currentIndex],
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: this._currentIndex,   //Select the index value corresponding to the configuration
          onTap: (int index){
              setState(() {  //Change state
                  this._currentIndex=index;
              });
          },
          iconSize:36.0,      //Size of icon
          fixedColor:Colors.red,  //Selected color  
          type:BottomNavigationBarType.fixed,   //The tabs at the bottom of the configuration can have multiple buttons
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text("home page")
            ),
             BottomNavigationBarItem(
              icon: Icon(Icons.category),
              title: Text("classification")
            ),
            
             BottomNavigationBarItem(
              icon: Icon(Icons.settings),
              title: Text("set up")
            )
          ],
        ),
      );
  }
}

route

Generally speaking, the route in fluent is page Jump. Manage route navigation through Navigator component in fluent.

It also provides a method to manage the stack. For example: navigator Push and navigator pop

Flutter provides us with two ways to configure route jump: 1. Basic route 2. Named route

Basic routing usage

For example, we now want to jump from the HomePage component to the SearchPage component.

1. Searchpage needs to be introduced into HomPage dart

import '../SearchPage.dart';

2. Jump through the following method in HomePage

RaisedButton(
      child: Text("Jump to search page"),
      onPressed: () {
        Navigator.of(context).push(
            MaterialPageRoute(
                builder: (context) => SerachPage()
            )
        );
      },
      color: Theme.of(context).accentColor,
      textTheme: ButtonTextTheme.primary,
    )

Basic route jump value

For example, we now want to jump from the HomePage component to the SearchPage component to pass values.

1. Searchpage needs to be introduced into HomPage dart

import '../SearchPage.dart';

2. Jump through the following method in HomePage

RaisedButton(
      child: Text("Jump to search page"),
      onPressed: () {
        Navigator.of(context).push(
            MaterialPageRoute(
            builder: (context) => SerachPage(title:"form ") //Pass value SerachPage plus constructor and pass parameters
            )
        );
      },
      color: Theme.of(context).accentColor,
      textTheme: ButtonTextTheme.primary,
    )

Named route

1. Configure routing

return MaterialApp(
      // home:Tabs(),
      initialRoute: '/', //Routes loaded during initialization
      routes: {
        '/':(contxt)=>Tabs(),
        '/search':(contxt) =>SearchPage(),
        '/form': (context) => FormPage(),
      },
    );

2. Route jump

RaisedButton(
            child: Text("Jump to search page"),
            onPressed: () {
              Navigator.pushNamed(context, '/search');
            },
            color: Theme.of(context).accentColor,
            textTheme: ButtonTextTheme.primary
        )d

Named route jump value

garish

Named routes are extracted separately into a file

Smoke a chicken

Return to the previous page

Navigator.of(context).pop();

Replace route

For example, we jump from the user center page to the registerFirst page, and then from the registerFirst page to the registerSecond page through pushReplacementNamed. At this time, when we click the return button of registerSecond, it will directly return to the user center.

Navigator.of(context).pushReplacementNamed('/registerSecond'); //Replace named route
// Normal route replacement
Navigator.of(context).pushReplacement(
            MaterialPageRoute(builder: (context)=>Second())
          );

Return to root route

For example, we jump from the user center to the registerFirst page, then jump from the registerFirst page to the registerSecond page, and then jump from registerSecond to the registerThird page. At this time, we want to return to the user center after registerThird is registered successfully. The method of returning to the root route is used at this time.

Navigator.of(context).pushAndRemoveUntil(
		new MaterialPageRoute(builder: (context) => new Tabs(index:1)),
		(route) => route == null
);

AppBar customize top button icon and color

attributedescribe
leadingA control displayed in front of the title usually displays the logo of the application on the home page; In other interfaces, it is usually displayed as a return button
titleTitle, usually displayed as the title text of the current interface, can be placed in components
actionsA control displayed after the title, usually represented by IconButton, can be placed in a button group
bottomUsually put tabBar, and a Tab navigation bar is displayed under the title
backgroundColorNavigation background color
iconThemeIcon style
textThemeText style
centerTitleIs the title centered
class AppBardemoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        leading: IconButton(
            icon: Icon(Icons.menu),
            tooltip: "Search",
            onPressed: () {
              print('Menu Pressed');
            }),
        title: Text('FlutterDemo'),
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.search),
              tooltip: "Search",
              onPressed: () {
                print('Search Pressed');
              }),
          IconButton(
              icon: Icon(Icons.more_horiz),
              tooltip: "more_horiz",
              onPressed: () {
                print('more_horiz Pressed');
              })
        ],
      ),
      body: Text('This is Appbar'),
    );
  }
}

Customize TabBar in AppBar to realize top Tab switching

TabBar common properties:

attributedescribe
tabsThe displayed label content generally uses Tab objects or other widgets
controllerTabController object
isScrollableWhether it is scrollable (it refers to scrolling appbar when there are many appbars, and scrolling appbar left and right. It is not scrolling content)
indicatorColorIndicator color
indicatorWeightIndicator height
indicatorPaddingPadding of bottom indicator
indicatorIndicator decoration, such as border, etc
indicatorSizeCalculation method of indicator size, tabbarindicatorsize The label is the same width as the text, tabbarindicator size Tabs are the same width as each tab
labelColorSelect the label color
labelStyleSelect the Style of label
labelPaddingpadding value of each label
unselectedLabelColorlabel color is not selected
unselectedLabelStylelabel's Style is not selected
import 'package:flutter/material.dart';

class AppBardemoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController( //DefaultTabController is after MaterialApp and before Scaffold
        length: 2,                //If the page is mounted via route, return DefaultTabController directly
        child: Scaffold(
          appBar: AppBar(
            title: TabBar(
              tabs: <Widget>[
                Tab(text: 'Hot'),
                Tab(text: "123"),
              ],
            ),
          ),
          body: TabBarView(
            children: <Widget>[
              ListView(
                children: <Widget>[
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab"))
                ],
              ),
              ListView(
                children: <Widget>[
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab"))
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Put the TabBar at the top of the navigation

Put the TabBar in the titile

import 'package:flutter/material.dart';

class AppBardemoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            isScrollable: true, //If there are multiple buttons, you can slide them
//            backgroundColor: Colors.red,
            leading: IconButton(
                icon: Icon(Icons.arrow_back),
                tooltip: "Search",
                onPressed: () {
                  Navigator.of(context).pop();
                }),
            title: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Expanded(
                    flex: 1,
                    child: TabBar(
                      tabs: <Widget>[Tab(text: "Hot"), Tab(text: "recommend")],
                    ))
              ],
            ),
          ),
          body: TabBarView(
            children: <Widget>[
              ListView(
                children: <Widget>[
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab"))
                ],
              ),
              ListView(
                children: <Widget>[
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab")),
                  ListTile(title: Text("This is the first one tab"))
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Another way to customize TabBar in AppBar to implement Tabs.

TabController needs to inherit stateful components

import 'package:flutter/material.dart';

class AppBardemoPage extends StatefulWidget {
  @override
  _AppBardemoPageState createState() => _AppBardemoPageState();
}

class _AppBardemoPageState extends State<AppBardemoPage>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _tabController = new TabController(
        vsync: this,
        length: 2
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("AppBardemoPage"),
        bottom: TabBar(
          controller: this._tabController, //be careful
          tabs: <Widget>[
            Tab(text: "Hot sale"), Tab(text: "recommend"),
          ],
        ),
      ),
      body: TabBarView(
        controller: this._tabController, //be careful
        children: <Widget>[
          Center(child: Text("Hot sale")),
          Center(child: Text("recommend"))
        ],
      ),
    );
  }
}

Shuttle drawer sidebar

In the Scaffold component, pass in the drawer parameter to define the left sidebar, and pass in endDrawer to define the right sidebar. The sidebar is hidden by default. We can slide the sidebar with our fingers or click the button to display the sidebar.

return Scaffold(
      appBar: AppBar(
        title: Text("Hello Flutter"),
      ),
      drawer: Drawer(
        child: Text("Left sidebar"),
      ),
      endDrawer: Drawer(
        child: Text("Right sidebar"),
      ),
    );

DrawerHeader

Common attributes:

attributedescribe
decorationSet top background color
childConfigure child elements
paddingpadding
marginMargin
drawer: Drawer(
        child: Column(
          children: <Widget>[
            DrawerHeader(
              decoration: BoxDecoration(
                  color: Colors.yellow,
                  image: DecorationImage(
                      image: NetworkImage(
                          "https://www.itying.com/images/flutter/2.png"),
                      fit: BoxFit.cover)),
              child: ListView(
                children: <Widget>[Text("I am a head")],
              ),
            ),
            ListTile(
              title: Text("Personal Center"),
              leading: CircleAvatar(
                child: Icon(Icons.people),
              ),
            ),
            Divider(),
            ListTile(
              title: Text("System settings"),
              leading: CircleAvatar(
                child: Icon(Icons.settings),
              ),
            ),
            Divider(),
          ],
        ),
      ),

UserAccountsDrawerHeader

attributedescribe
decorationTop background color setting
accountNametitle of account
accountEmailAccount mailbox
currentAccountPictureUser Avatar
otherAccountsPicturesUsed to set the current account and other account avatars
margin
drawer: Drawer(
        child: Column(
          children: <Widget>[
            UserAccountsDrawerHeader(
              accountName: Text("Meow meow?"),
              accountEmail: Text("xxx@xxx.com"),
              currentAccountPicture: CircleAvatar(// It is automatically processed into a circle, and there is no need to set the picture fit
                backgroundImage:
                    NetworkImage("https://www.itying.com/images/flutter/3.png"),
              ),
              decoration: BoxDecoration(
                  color: Colors.yellow,
                  image: DecorationImage(
                      image: NetworkImage(
                          "https://www.itying.com/images/flutter/2.png"),
                      fit: BoxFit.cover)),
              otherAccountsPictures: <Widget>[
                Image.network("https://www.itying.com/images/flutter/4.png"),
                Image.network("https://www.itying.com/images/flutter/5.png"),
                Image.network("https://www.itying.com/images/flutter/6.png"),
              ],
            ),
            ListTile(
              title: Text("Personal Center"),
              leading: CircleAvatar(
                child: Icon(Icons.people),
              ),
            ),
            Divider(),
            ListTile(
              title: Text("System settings"),
              leading: CircleAvatar(
                child: Icon(Icons.settings),
              ),
            ),
            Divider(),
          ],
        ),
      ),

Sidebar route jump

onTap: () {
                Navigator.of(context).pop();
                Navigator.pushNamed(context, "/tabBarController");
              },

Introduction to button components

There are many Button components in the shutter. The common Button components are: RaisedButton, FlatButton, IconButton, outlinbutton, ButtonBar, FloatingActionButton, etc.

RaisedButton: the raised Button is actually a Button in Material Design style

FlatButton: flattened button

OutlineButton: wireframe button

IconButton: icon button

ButtonBar: button group

FloatingActionButton: floating button

Some properties in the button component

Attribute nameValue typeAttribute value
onPressedVoidCallback, which generally receives a methodRequired parameter, the callback triggered when the button is pressed, receives a method, passes null to indicate that the button is disabled, and the disabled related style will be displayed
childWidgetText control
textColorColortext color
colorColorButton color
disabledColorColorColor when button is disabled
disabledTextColorColorText color when button is disabled
splashColorColorThe color of the water ripple when the button is clicked
highlightColorColorThe color of the button after clicking (long press) the button
elevationdoubleThe range of shadows. The larger the value, the larger the shadow range
paddingpadding
shapeSets the shape of the button
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10),
)/ / fillet button
shape: CircleBorder(
side: BorderSide(
color: Colors.white,
)
)/ / round button
class ButtonDemoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Button demo page"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text('Normal button'),
                  onPressed: () {
                    print('Click');
                  },
                ),
                SizedBox(width: 20),
                RaisedButton(
                  child: Text('Colored buttons'),
                  textColor: Colors.white,
                  color: Colors.blue,
                  onPressed: () {
                    print('Click');
                  },
                ),
                SizedBox(width: 20),
                RaisedButton(
                  child: Text('Shadow button'),
                  textColor: Colors.white,
                  color: Colors.blue,
                  elevation: 10,
                  onPressed: () {
                    print('Click');
                  },
                )
              ],
            ),
            SizedBox(height: 40),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                    height: 60,
                    width: 200,
                    child: RaisedButton(
                      child: Text('Button with width and height'),
                      textColor: Colors.white,
                      color: Colors.blue,
                      elevation: 10,
                      onPressed: () {
                        print('Click');
                      },
                    ))
              ],
            ),
            SizedBox(height: 40),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                    child: Container(
                  height: 60,
                  margin: EdgeInsets.all(20),
                  child: RaisedButton(
                    child: Text('Full screen button'),
                    textColor: Colors.white,
                    color: Colors.blue,
                    elevation: 10,
                    onPressed: () {
                      print('Click');
                    },
                  ),
                ))
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                    child: Container(
                  height: 60,
                  margin: EdgeInsets.all(20),
                  child: RaisedButton(
                    child: Text('Button with rounded corners'),
                    textColor: Colors.white,
                    color: Colors.blue,
                    elevation: 10,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    onPressed: () {
                      print('Click');
                    },
                  ),
                ))
              ],
            )
          ],
        ),
      ),
    );
  }
}

Introduction to FloatingActionButton

The FloatingActionButton, referred to as FAB for short, can realize the floating button and navigation similar to the free fish app

Attribute nameAttribute value
childThe subview is generally Icon, and text is not recommended
tooltipFAB is displayed on time, which is also a barrier free function
backgroundColorbackground color
elevationShadow when not clicked
hignlightElevationWhen clicked, the shadow value is 12.0 by default
onPressedClick event callback
shapeThe shape of FAB can be defined
miniIs it a mini type? The default is false

Introduction to common forms

Common forms in fluent include TextField single line text box, TextField multi line text box, CheckBox, Radio, SwitchCheckboxListTile, RadioListTile, SwitchListTile and Slide

TextField textbox component

TextField form common properties:

attributedescribe
maxLinesSetting this parameter can change the text box into a multi line text box
onChangedEvent triggered when the text box changes
decorationhintText is similar to placeholder in html
Border configures the border of the text box. It is used with OutlineInputBorder
LabelText label name
labelStyle configure the style of label
obscureTextobscureText
controllercontroller combined with TextEditingController() can configure the content displayed by default in the form
TextField(
        maxLines: 10,
//      obscureText: true,
        decoration:
        InputDecoration(
            hintText: "Password box",
            border: OutlineInputBorder()
        ),
      )d
  var _username = TextEditingController();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _username.text = 'This is the initial value of the text box';
  }
TextField(
          controller: _username,
          onChanged: (value) {
//            print(value);
            setState(() {
              this._username.text = value;
            });
          },
          decoration: InputDecoration(
            hintText: "Please enter your content",
          ),
        )

Checkbox, CheckboxListTile multi box components

Common Checkbox properties:

attributedescribe
valuetrue or false
onChangedEvent triggered when changing
activeColorSelected color, background color
checkColorThe selected color and the check mark color in the Checkbox

Common properties of CheckboxListTile:

attributedescribe
valuetrue or false
onChangedEvent triggered when changing
activeColorSelected color, background color
titletitle
subtitleSecondary title
secondaryConfigure icons or pictures
selectedDoes the text color change when selected
Checkbox(
          value: _isSelected,
          onChanged: (v) {
            print(v);
            setState(() {
              this._isSelected = v;
            });
          },
          activeColor: Colors.red,
          checkColor: Colors.blue,
        )d
CheckboxListTile(
          value: _isSelected,
          title: Text("This is a title"),
          subtitle: Text("This is the secondary title"),
          onChanged: (v) {
            setState(() {
              this._isSelected = v;
            });
          },
          activeColor: Colors.red,
          secondary:
              Image.network("https://www.itying.com/images/flutter/1.png"),
          selected: _isSelected,
        )

Radio and RadioListTile radio button components

Radio common properties:

attributedescribe
valueSingle choice value
onChangedTriggered on change
activeColorSelected color, background color
groupValueSelect the value of the group

RadioListTile common properties:

attributedescribe
valuetrue or false
onChangedEvent triggered when changing
activeColorSelected color, background color
titletitle
subtitleSecondary title
secondaryConfigure icons or pictures
groupValueSelect the value of the group
int _groupValue=1;
Radio(
              value: 0,
              onChanged: (v) {
                setState(() {
                  this._groupValue = v;
                });
              },
              activeColor: Colors.red,
              groupValue: _groupValue,
            ),
Radio(
              value: 1,
              onChanged: (v) {
                setState(() {
                  this._groupValue = v;
                });
              },
              activeColor: Colors.red,
              groupValue: _groupValue,
            )
int _groupValue = 1;

  _handelChange(v) {
    setState(() {
      _groupValue = v;
    });
  }

RadioListTile(
              value: 1,
              title: Text("nodejs Video tutorial"),
              subtitle: Text("egg.js Video tutorial"),
              secondary:
                  Image.network("https://www.itying.com/images/flutter/1.png"),
              groupValue: _groupValue,
              onChanged: _handelChange,
            ),
            Divider(),
            RadioListTile(
              value: 0,
              title: Container(
                height: 60,
                child: Text("This is the text"),
                color: Colors.red,
              ),
              subtitle: Text("egg.js Video tutorial"),
              secondary:
                  Image.network("https://www.itying.com/images/flutter/1.png"),
              groupValue: _groupValue,
              onChanged: _handelChange,
            )

Switch

attributedescribe
valueSingle choice value
onChangedTriggered on change
activeColorSelected color, background color

Date and time stamp

Date to timestamp:

 var now = new DateTime.now();
 print(now.millisecondsSinceEpoch);//In milliseconds, 13 bit timestamp

Time stamp to date:

var now = new DateTime.now();
var a=now.millisecondsSinceEpoch; //time stamp

print(DateTime.fromMillisecondsSinceEpoch(a));

Third party library date_ Use of format

file: https://pub.dev/packages/date_format

Call the built-in date component and time component

Date component:

var _datetime = DateTime.now();

  _showDatePicker() async {
    var date = await showDatePicker(
        context: context,
        initialDate: _datetime,
        firstDate: DateTime(1900),
        lastDate: DateTime(2050));
    if (date == null) return;
    print(date);
    setState(() {
      _datetime = date;
    });
  }

Time component:

var _time = TimeOfDay(hour: 9, minute: 20);

  _showTimePicker() async {
    var time = await showTimePicker(context: context, initialTime: _time);
    if (time == null) return;
    print(time);
    setState(() {
      this._time = time;
    });
  }
Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              InkWell(
                child: Row(
                  children: <Widget>[
                    Text("${formatDate(_datetime, [yyyy, '-', mm, '-', dd])}"),
                    Icon(Icons.arrow_drop_down)
                  ],
                ),
                onTap: _showDatePicker,
              ),
              InkWell(
                child: Row(
                  children: <Widget>[
                    Text("${this._time.format(context)}"),
                    Icon(Icons.arrow_drop_down)
                  ],
                ),
                onTap: _showTimePicker,
              )
            ],
          )
        ],
      )

Call the built-in date component and time component to change to Chinese

http://bbs.itying.com/topic/5cfb2a12f322340b2c90e764

Call the third-party time component

https://pub.dev/packages/flutter_cupertino_date_picker

Too lazy to remember...

Carousel chart component

Address: https://pub.dev/packages/flutter_swiper

import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';

class SwiperPage extends StatefulWidget {
  SwiperPage({Key key}) : super(key: key);

  _SwiperPageState createState() => _SwiperPageState();
}

class _SwiperPageState extends State<SwiperPage> {
  List<Map> list = [
    {"url": "https://www.itying.com/images/flutter/1.png"},
    {"url": "https://www.itying.com/images/flutter/2.png"},
    {"url": "https://www.itying.com/images/flutter/3.png"}
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Carousel diagram component demonstration'),
      ),
      body: Column(
        children: <Widget>[
          Container(
            width: double.infinity,
            child: AspectRatio(
              aspectRatio: 16 / 9,
              child: new Swiper(
                itemBuilder: (BuildContext context, int index) {
                  return new Image.network(
                    this.list[index]["url"],
                    fit: BoxFit.fill,
                  );
                },
                itemCount: list.length,
                pagination: new SwiperPagination(),
                autoplay: true,
								// control: new SwiperControl(),
              ),
            ),
          )
        ],
      ),
    );
  }
}

1, AlertDialog

var alertRel = await showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text("Tips!"),
            content: Text("Are you sure you want to delete"),
            actions: <Widget>[
              FlatButton(
                child: Text("cancel"),
                onPressed: () {
                  Navigator.pop(context, 'Cancle');
                },
              ),
              FlatButton(
                child: Text("determine"),
                onPressed: () {
                  Navigator.pop(context, 'Ok');
                },
              )
            ],
          );
        });

2, SimpleDialog

var simpleRel = await showDialog(
        context: context,
        builder: (BuildContext context) {
          return SimpleDialog(
            title: Text("select Radio button box"),
            children: <Widget>[
              SimpleDialogOption(
                child: Text("Option A"),
                onPressed: () {
                  Navigator.pop(context, 'Option A');
                },
              ),
              Divider(),
              SimpleDialogOption(
                child: Text("Option B"),
                onPressed: () {
                  Navigator.pop(context, 'Option B');
                },
              ),
              Divider(),
              SimpleDialogOption(
                child: Text("Option C"),
                onPressed: () {
                  Navigator.pop(context, 'Option C');
                },
              )
            ],
          );
        });

3, showModalBottomSheet

var actionSheet = await showModalBottomSheet(
        context: context,
        builder: (builder) {
          return Container(
            height: 200, //The height is not set to display half
            child: Column(
              children: <Widget>[
                ListTile(
                  title: Text("share A"),
                  onTap: () {
                    Navigator.pop(context, 'A');
                  },
                ),
                ListTile(
                  title: Text("share B"),
                  onTap: () {
                    Navigator.pop(context, 'B');
                  },
                ),
                ListTile(
                  title: Text("share C"),
                  onTap: () {
                    Navigator.pop(context, 'C');
                  },
                )
              ],
            ),
          );
        });

4, showToast

https://pub.dev/packages/fluttertoast

Fluttertoast.showToast(
        msg: "This is Short Toast",
        toastLength: Toast.LENGTH_SHORT,
        timeInSecForIos: 1);

Custom Dialog

To customize the Dialog object, you need to inherit the Dialog class. Although Dialog provides the child parameter to write the view interface, it often fails to achieve the desired effect, because the default Dialog background box is full screen. If we want to fully define the interface, we need to rewrite the build function.

import 'package:flutter/material.dart';

class LoadingDialog extends Dialog {
  final String text;

  LoadingDialog(this.text);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Material(
//Create transparent layer
      type: MaterialType.transparency, //Transparent type child: new center(
      child: Container(
        width: 300,
        height: 200,
        color: Colors.white,
        child: Column(
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(10),
              child: Stack(
                children: <Widget>[
                  Align(
                    alignment: Alignment.center,
                    child: Text("About us"),
                  ),
                  Align(
                    alignment: Alignment.centerRight,
                    child: InkWell(
                      child: Icon(Icons.close),
                      onTap: () {
                        Navigator.pop(context);
                      },
                    ),
                  )
                ],
              ),
            ),
            Divider(),
            Column(
              children: <Widget>[
                Container(
                  height: 40,
                  child: Text(this.text),
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

timer

import 'dart:async';

_showTimer(context) {
    var timer;
    timer = Timer.periodic(Duration(milliseconds: 1500), (t) {
      print('implement');
      Navigator.pop(context);
      t.cancel();
    });
  }

Timer combined with Dialog

import 'dart:async';

import 'package:flutter/material.dart';

class LoadingDialog extends Dialog {
  final String text;

  LoadingDialog(this.text);

  _showTimer(context) {
    var timer;
    timer = Timer.periodic(Duration(milliseconds: 1500), (t) {
      print('implement');
      Navigator.pop(context);
      t.cancel();
    });
  }

  @override
  Widget build(BuildContext context) {
    _showTimer(context);
    return Material(
      //Create transparent layer
      type: MaterialType.transparency, //Transparent type
      child: Center(
        child: Container(
          width: 300,
          height: 200,
          color: Colors.white,
          child: Column(
            children: <Widget>[
              Padding(
                padding: EdgeInsets.all(10),
                child: Stack(
                  children: <Widget>[
                    Align(
                      alignment: Alignment.center,
                      child: Text("About us"),
                    ),
                    Align(
                      alignment: Alignment.centerRight,
                      child: InkWell(
                        child: Icon(Icons.close),
                        onTap: () {
                          Navigator.pop(context);
                        },
                      ),
                    )
                  ],
                ),
              ),
              Divider(),
              Column(
                children: <Widget>[
                  Container(
                    height: 40,
                    child: Text(this.text),
                  )
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

Conversion of JSON string and Map type (small project)

(small projects are transferred directly, and large projects use model classes. The notes are below)

import 'dart:convert';

var mapData = {"name": "Zhang San", "age": "20"};
var strData = '{"name":"Zhang San","age":"20"}';
print(json.encode(mapData)); //Convert Map to Json string
print(json.decode(strData)); //Convert Json string to Map type

Network request using http Library

Please refer to the official documentation: https://pub.dev/packages/http

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);

  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _news = '';

  //Request data
  _getData() async {
    var apiUrl = "http://127.0.0.1:8080/";

    var result = await http.get(apiUrl);
    if (result.statusCode == 200) {
      // print(json.decode(result.body));
      setState(() {
        this._news = json.decode(result.body)["msg"];
      });
    } else {
      print(result.statusCode);
    }
  }

  //Submit data
  _postData() async {
    var apiUrl = "http://127.0.0.1:8080/x";

    var result = await http.post(apiUrl, body: {'username': 'Zhang San', 'age': '20'});
    if (result.statusCode == 200) {
      print(json.decode(result.body));
    } else {
      print(result.statusCode);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(this._news),
          RaisedButton(
            child: Text('Get Request data'),
            onPressed: _getData,
          ),
          SizedBox(height: 20),
          RaisedButton(
            child: Text('Post Submit data'),
            onPressed: _postData,
          ),
          SizedBox(height: 20),
          RaisedButton(
            child: Text('Get Request data, render data presentation demo'),
            onPressed: () {
              Navigator.pushNamed(context, '/http');
            },
          ),
          SizedBox(height: 20),
        ],
      ),
    );
  }
}

map traversal, outer set ListView component

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class HttpDemo extends StatefulWidget {
  HttpDemo({Key key}) : super(key: key);

  _HttpDemoState createState() => _HttpDemoState();
}

class _HttpDemoState extends State<HttpDemo> {
  List _list = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();
  }

  _getData() async {
    var apiUrl = "http://a.itying.com/api/productlist";
    var result = await http.get(apiUrl);
    if (result.statusCode == 200) {
      print(result.body);
      setState(() {
        this._list = json.decode(result.body)["result"];

        /*
      {
        "result": [{
            "_id": "5ac0896ca880f20358495508",
            "title": "Selected hot dishes ",
            "pid": "0",		
          }, {
            "_id": "5ac089e4a880f20358495509",
            "title": "Specialties ",
            "pid": "0",
            
          }
        ]
      }

      */
      });
    } else {
      print("fail ${result.statusCode}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Request data Demo"),
        ),
        body: this._list.length > 0
            ? ListView(
                children: this._list.map((value) { //map traversal, outer set ListView component
                  return ListTile(
                    title: Text(value["title"]),
                  );
                }).toList(),
              )
            : Text("Loading..."));
  }
}

ListView.builder

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class HttpDemo extends StatefulWidget {
  HttpDemo({Key key}) : super(key: key);

  _HttpDemoState createState() => _HttpDemoState();
}

class _HttpDemoState extends State<HttpDemo> {
  List _list = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();
  }

  _getData() async {
    var apiUrl = "http://a.itying.com/api/productlist";
    var result = await http.get(apiUrl);
    if (result.statusCode == 200) {
      print(result.body);
      setState(() {
        this._list = json.decode(result.body)["result"];

        /*
      {
        "result": [{
            "_id": "5ac0896ca880f20358495508",
            "title": "Selected hot dishes ",
            "pid": "0",		
          }, {
            "_id": "5ac089e4a880f20358495509",
            "title": "Specialties ",
            "pid": "0",
            
          }
        ]
      }

      */
      });
    } else {
      print("fail ${result.statusCode}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Request data Demo"),
        ),
        body: this._list.length > 0
            ? ListView.builder(
                itemCount: this._lidst.length,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text("${this._list[index]["title"]}"),
                  );
                },
              )
            : Text("Loading..."));
  }
}

Dio Library

dio is a powerful Dart Http request library, which supports Restful API, FormData, interceptor, request cancellation, Cookie management, file upload / download, timeout, custom adapter, etc

https://pub.dev/packages/dio

https://github.com/flutterchina/dio/blob/master/README-ZH.md

Do not make chestnuts, according to the latest official document chestnuts

Pull down refresh and pull up paging

RefreshIndicator, a component for pull-down refresh, is provided in the official sdk of Flutter. However, there is no pull-up page to load more components. However, there is a ScrollController attribute in the shuttle ListView, which is specifically used to control the sliding event of the ListView. Here, we can judge whether it slides to the bottom according to the position of the ListView to load more processing.

Api interface: http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1

Pull down refresh

@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Request data Dio Demo"),
        ),
        body: this._list.length > 0
            ? RefreshIndicator(
                onRefresh: _onRefresh,
                child: ListView.builder(
                    itemCount: this._list.length,
                    itemBuilder: (context, index) {
                      return ListTile(title: Text(this._list[index]["title"]));
                    }))
            : Text("Loading..."));
  }

  Future<void> _onRefresh() async {
    print('Perform refresh');
  }

Pull up page load more

_ scrollController.position.pixels scrolling distance

_ scrollController. position. Maxscrollextend total distance

Core code

ScrollController _scrollController = ScrollController(); //listview controller
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();
    
    //Listen for scroll bar events
    _scrollController.addListener(() {
      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 20) {
        print("Scroll to the bottom");
        _getData();
      }
    });
  }

@override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose(); //No, cut it off to improve the performance
  }

ListView.builder(
                  itemCount: this._list.length,
                  controller: _scrollController, //be careful
                  itemBuilder: (context, index) {
                    
                  }
                )

Complete code

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'dart:convert';

class NewsPage extends StatefulWidget {
  @override
  _NewsPageState createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  ScrollController _scrollController = ScrollController(); //listview controller

  List _list = [];
  int _page = 1;
  bool isLoading = true; //Is data loading
  
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();

    //Listen for scroll bar events
    _scrollController.addListener(() {
      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 20) {
        print("Slide to the bottom");
        _getData();
      }
    });
  }
  
  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose(); //No, cut it off to improve the performance
  }

  _getData() async {
    String apiUrl =
        "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${this._page}";
    Response result = await Dio().get(apiUrl);
    var res = json.decode(result.data)["result"];

//    print(json.decode(result.data)["result"]);
    setState(() {
      this._list.addAll(res);
      this._page++;
    });
    //Determine whether it is the last page
    if (res.length < 20) {
      setState(() {
        this.isLoading = false;
      });
    }
  }

  Widget _getMoreWidget() {
    if (isLoading) {
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                'Loading...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );
    } else {
      return Center(
        child: Text("--I have a bottom line--"),
      );
    }
  }

  //Pull down refresh
  Future<void> _onRefresh() async {
    print("Perform refresh");
    this._getData();
    await Future.delayed(Duration(seconds: 3), () {
      print("refresh");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Request data Dio Demo"),
      ),
      body: this._list.length > 0
          ? RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._list.length,
                itemBuilder: (context, index) {
                  if (index == this._list.length - 1) {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text(this._list[index]["title"], maxLines: 1),
                          onTap: () {
                            Navigator.pushNamed(context, '/newsContent');
                          },
                        ),
                        Divider(),
                        _getMoreWidget()
                      ],
                    );
                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text(this._list[index]["title"], maxLines: 1),
                          onTap: () {
                            Navigator.pushNamed(context, '/newsContent');
                          },
                        ),
                        Divider()
                      ],
                    );
                  }
                },
              ),
            )
          : _getMoreWidget(),
    );
  }
}

Resolve duplicate requests

//Resolve duplicate requests
  bool flag=true;
  
  @override
  void initState() {
    super.initState();
    _getData();

    //Listen for scroll bar scrolling events
    _scrollController.addListener((){
        //_ scrollController.position.pixels / / get the scroll height of the scroll bar
        //_ scrollController. position. Maxscrollextend / / get the page height 
        if(_scrollController.position.pixels>_scrollController.position.maxScrollExtent-20){
          if(this.flag && this._hasMore){ //If requested, no more data will be obtained
            _getData();
          }
        }
    });
  }


@override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose(); //No, cut it off to improve the performance
  }

//get data
  _getData() async {
    // The request data is preceded by false
    setState(() {
     this.flag=false; 
    });

    var api ='url';
    var result = await Dio().get(api);
    . . . 
      // Set to true after the data request is completed
       setState(() {
        . . . 
        this.flag=true; 
      });   
  }

Scroll bar back to top

//Back to the top
_scrollController.jumpTo(0);

Reference code

  //Triggered when the navigation changes
  _subHeaderChange(id) {
    if (id == 4) {
      _scaffoldKey.currentState.openEndDrawer();
      setState(() {
        this._selectHeaderId = id;
      });
    } else {
      setState(() {
        this._selectHeaderId = id;
        this._sort ="${this._subHeaderList[id - 1]["fileds"]}_${this._subHeaderList[id - 1]["sort"]}";

        //Reset paging
        this._page = 1;
        //Reset Data 
        this._productList = [];
        //Change sort sort
        this._subHeaderList[id - 1]['sort'] =
            this._subHeaderList[id - 1]['sort'] * -1;
        //Back to the top
        _scrollController.jumpTo(0);
        //Reset_ hasMore
        this._hasMore = true;
        //Re request
        this._getProductListData();
      });
    }
  }

Implement a simple news APP

api interfaces involved:

News list: http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1

News details: http://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=20

List page

import 'package:cc/pages/NewsContent.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'dart:convert';

class NewsPage extends StatefulWidget {
  @override
  _NewsPageState createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  ScrollController _scrollController = ScrollController(); //listview controller

  List _list = [];
  int _page = 1;
  bool isLoading = true; //Is data loading

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();

    //Listen for scroll bar events
    _scrollController.addListener(() {
      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 20) {
        print("Slide to the bottom");
        _getData();
      }
    });
  }

  _getData() async {
    String apiUrl =
        "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${this._page}";
    Response result = await Dio().get(apiUrl);
    var res = json.decode(result.data)["result"];

//    print(json.decode(result.data)["result"]);
    setState(() {
      this._list.addAll(res);
      this._page++;
    });
    //Determine whether it is the last page
    if (res.length < 20) {
      setState(() {
        this.isLoading = false;
      });
    }
  }

  Widget _getMoreWidget() {
    if (isLoading) {
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                'Loading...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );
    } else {
      return Center(
        child: Text("--I have a bottom line--"),
      );
    }
  }

  //Pull down refresh
  Future<void> _onRefresh() async {
    print("Perform refresh");
    this._getData();
    await Future.delayed(Duration(seconds: 3), () {
      print("refresh");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Request data Dio Demo"),
      ),
      body: this._list.length > 0
          ? RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._list.length,
                itemBuilder: (context, index) {
                  if (index == this._list.length - 1) {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text(this._list[index]["title"], maxLines: 1),
                          onTap: () {
                            Navigator.pushNamed(context, '/newsContent');
                          },
                        ),
                        Divider(),
                        _getMoreWidget()
                      ],
                    );
                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text(this._list[index]["title"], maxLines: 1),
                          onTap: () {
                            Navigator.of(context).push(MaterialPageRoute(
                                builder: (context) =>
                                    NewsContent(this._list[index]["aid"])));
                          },
                        ),
                        Divider()
                      ],
                    );
                  }
                },
              ),
            )
          : _getMoreWidget(),
    );
  }
}

Detail page

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:dio/dio.dart';

class NewsContent extends StatefulWidget {
  var aid;

  NewsContent(this.aid);

  createState() => _NewsContentState();
}

class _NewsContentState extends State<NewsContent> {
  List list = [];

  _getData() async {
    String apiUrl =
        "http://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=${this.widget.aid}";
    Response response = await Dio().get(apiUrl);
    setState(() {
      this.list = json.decode(response.data)["result"];
    });
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(
          title: Text(this.list.length > 0 ? this.list[0]["title"] : ""),
        ),
        body: ListView(
          children: <Widget>[
            Text(this.list.length > 0 ? this.list[0]["content"] : "")
          ],
        ),
      ),
    );
  }
}

Parsing html

https://pub.dev/packages/flutter_html

chicken ribs... Only partial HTML tags can be parsed

Use of WebView component inappbrowser

WebView is recommended_ The official library of flutter, inappbrowser, has become warped

https://pub.dev/packages/flutter_inappbrowser

ios simulator test failed...

Get device information

https://pub.dev/packages/device_info

Use Gaode positioning preparation to obtain key

1. Apply to become a developer

2. Create application configuration and get Key (refer to tutorial demonstration)

https://lbs.amap.com/api/android-sdk/guide/create-project/get-key

Realize positioning with Gaode

https://pub.dev/packages/amap_location

image_picker realizes camera photography and album selection

https://pub.dev/packages/image_picker

/*photograph*/
  _takePhoto() async {
    var image = await ImagePicker.pickImage(source: ImageSource.camera);
    setState(() {
      _imgPath = image;
    });
  }

/*album*/
  _openGallery() async {
    var image = await ImagePicker.pickImage(source: ImageSource.gallery);
    setState(() {
      _imgPath = image;
    });
  }

Upload pictures to the server

https://pub.dev/packages/dio

Dio2.x and 3 Different x codes shall be subject to DIO official documents

//Upload pictures
  _uploadImage(File _imageDir) async {
    //Note: DIO3 In order to be compatible with the web, version x has made some modifications. When uploading pictures, you need to convert the File type into String type. The specific code is as follows

    var fileDir = _imageDir.path;

    FormData formData = FormData.fromMap({
      "name": "zhangsna 6666666666",
      "age": 20,
      "sex": "male",
      "file": await MultipartFile.fromFile(fileDir, filename: "xxx.jpg")
    });
    var response =
        await Dio().post("http://jd.itying.com/imgupload", data: formData);
    print(response);
  }

Video playback

video_ The official library of player, which supports fluent Web

A video is officially provided in fluent_ The player plug-in can play videos. But video_player has some limitations. Can't control the bottom playback progress, etc. Therefore, we mainly explain a third-party video playback library chewie. chewie is an unofficial third-party video playback component, which looks like a component based on HTML5 playback. chewie relative video_ For player, it has the functions of control bar and full screen. Using chewie_ Player engine and wrap it in a friendly Material or Cupertino UI!

https://pub.dev/packages/video_player

https://pub.dev/packages/chewie

iOS warning

The video player plug-in used by chewie does not work on iOS simulator. iOS devices must be used during development / testing.

coffee 21:44:59
Amway flutter_ijkplayer video player has tried many in the past two days, but this has solved the current problem

coffee 22:31:46
flutter_ Tencent player Tencent cloud ios cannot play live streaming of rtmp protocol
coffee 22:33:13
Official Video_ The player has no ui and needs to be implemented by itself
coffee 22:34:47
chewie wrapped a layer of video_ The player provides a ui. I used this for video playback. It took a lot of effort to play it in the original video size. Today, I found the flutter_ijkplayer is easy to use, but the playback function is not going to be changed for the time being. It's not easy to adjust it

chewie video playback full demo

import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';

class ChewieVideoDemo extends StatefulWidget {
  ChewieVideoDemo({Key key}) : super(key: key);

  _ChewieVideoDemoState createState() => _ChewieVideoDemoState();
}

class _ChewieVideoDemoState extends State<ChewieVideoDemo> {
  VideoPlayerController videoPlayerController;
  ChewieController chewieController;

  @override
  void initState() {
// TODO: implement initState super.initState();
    videoPlayerController = VideoPlayerController.network(
        'http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4');
    chewieController = ChewieController(
      videoPlayerController: videoPlayerController,
      aspectRatio: 3 / 2,
      autoPlay: true,
      looping: true,
    );
  }

  @override
  void dispose() {
// TODO: implement dispose super.dispose();
    videoPlayerController.dispose();
    chewieController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('title'),
      ),
      body: Center(
        child: Chewie(
          controller: chewieController,
        ),
      ),
    );
  }
}

Detection network

https://pub.dev/packages/connectivity

Detect network integrity demo

import 'package:flutter/material.dart';
import 'package:connectivity/connectivity.dart';

class NetworkPage extends StatefulWidget {
  NetworkPage({Key key}) : super(key: key);

  _NetworkPageState createState() => _NetworkPageState();
}

class _NetworkPageState extends State<NetworkPage> {
  String _state;
  var _subscription;

  @override
  initState() {
    super.initState();
    _subscription = Connectivity()
        .onConnectivityChanged
        .listen((ConnectivityResult result) {
      // Got a new connectivity status!
      if (result == ConnectivityResult.mobile) {
        setState(() {
          _state = "Mobile network";
        });
// I am connected to a mobile network. } else if (result == ConnectivityResult.wifi) {
        setState(() {
          _state = "Wifi network";
        });
// I am connected to a wifi network. }else{
        setState(() {
          _state = "No network";
        });
      }
    });
  }

  @override
  dispose() {
    super.dispose();
    _subscription.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Detect network changes"),
      ),
      body: Text("${_state}"),
    );
  }
}

Local storage

https://pub.dev/packages/shared_preferences

be careful:

If SharedPreferences prefs = await SharedPreferences getInstance(); Written in the outer layer of runapp(),

To add

Some methods commonly used in local storage

1. Set value

SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(key, value);
prefs.setBool(key, value)
prefs.setDouble(key, value)
prefs.setInt(key, value)
prefs.setStringList(key, value)

2. Get value

SharedPreferences prefs = await SharedPreferences.getInstance();
var data=prefs.getString("name");

3. Delete value

SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove(key); //Delete key assignment
prefs.clear();//Clear key value pair

Store full demo locally

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class StoragePage extends StatefulWidget {
  StoragePage({Key key}) : super(key: key);

  _StoragePageState createState() => _StoragePageState();
}

class _StoragePageState extends State<StoragePage> {
  _saveData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString("name", "Zhang San");
// prefs.setBool(key, value)
// prefs.setDouble(key, value)
    // prefs.setInt(key, value)
// prefs.setStringList(key, value)
  }

  _getData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var data = prefs.getString("name");
    print(data);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Local storage"),
      ),
      body: Center(
        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
          RaisedButton(
            child: Text('Save data'),
            onPressed: _saveData,
          ),
          SizedBox(height: 10),
          RaisedButton(
            child: Text('get data'),
            onPressed: _getData,
          )
        ]),
      ),
    );
  }
}

Scan QR code barcode plug-in

https://pub.dev/packages/barcode_scan

There are a lot of mistakes, great modifications, and no records... Use it again

Detect the application version number, download files from the server, and realize the automatic upgrade and installation of App

1. Android App upgrade execution process

1. Get local version number

2. Request the server to get the server version number

3. If the local version is inconsistent with the server version, you will be prompted to upgrade, and a pop-up window will prompt you whether to update

4. The user decides to upgrade and calls the file transfer method to download the apk file

5. Monitor download progress

6. After downloading, open Apk for installation

Note: you can't download and install directly in Ios. If the version is inconsistent, you can directly jump to the application market corresponding to Ios application.

Configuration version number: (the version number obtained by the fluent application is not here, but in the pubspec.yaml file)

<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" package="io.jdshop.demo" xmlns:android="http://schemas.android.com/apk/res/android">

2. Preparation before upgrading app configuration permissions

Configure androidmenifest XML file

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

3. API libraries involved in Android app upgrade

Plug in namedescribePlug in address
package_infoTest version numberhttps://pub.dev/packages/package_info
path_providerGet file storage pathhttps://pub.dev/packages/path_provider
flutter_downloaderflutter_downloaderhttps://pub.dev/packages/flutter_downloader
open_fileOpen file plug-inhttps://pub.dev/packages/open_file

4. Get version information

https://pub.dev/packages/package_info

PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
print("appName:${appName}");
print("packageName:${packageName}");
print("version:${version}");
print("buildNumber:${buildNumber}");

5. Get file storage path

https://pub.dev/packages/path_provider

Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;

Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;

var directory = await getExternalStorageDirectory();

String storageDirectory=directory.path;

print("tempPath:${tempPath}");

print("appDocDir:${appDocPath}");

print("StorageDirectory:${storageDirectory}");

6. Download File

https://pub.dev/packages/flutter_downloader

final directory = await getExternalStorageDirectory();
    String _localPath = directory.path;

    final taskId = await FlutterDownloader.enqueue(
      url: "http://www.ionic.wang/jdshop.apk",
      savedDir: _localPath,
      showNotification:
          true, // show download progress in status bar (for Android)
      openFileFromNotification:
          true, // click on notification to open downloaded file (for Android)

7. Open file

https://pub.dev/packages/open_file

OpenFile.open("${_localPath}/jdshop.apk");

8. Precautions

1. The App version of the server must be greater than the local App version

2. The package names and signatures of the local App and the server App must be consistent, so that the server package can replace the local package.

Complete code

import 'package:flutter/material.dart';

import 'package:package_info/package_info.dart';

import 'package:path_provider/path_provider.dart';
import 'dart:io';

import 'package:open_file/open_file.dart';

import 'package:flutter_downloader/flutter_downloader.dart';

class AppVersionPage extends StatefulWidget {
  AppVersionPage({Key key}) : super(key: key);

  _AppVersionPageState createState() => _AppVersionPageState();
}

class _AppVersionPageState extends State<AppVersionPage> {
  @override
  void initState() {
    // TODO: implement initState

    super.initState();

    this._getPackageInfo();

    this._getAppPath();
  }

  //Pop up Dialog
  _showDialog() async {
    var alertRel = await showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text("to update APP Tips!"),
            content: Text("A new version was found. The new version fixes the following bug Update!"),
            actions: <Widget>[
              FlatButton(
                child: Text("no"),
                onPressed: () {
                  Navigator.pop(context, 'Cancle');
                },
              ),
              FlatButton(
                child: Text("yes"),
                onPressed: () {
                  Navigator.pop(context, 'Ok');
                },
              )
            ],
          );
        });
  }

  //Get version number
  _getPackageInfo() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    String appName = packageInfo.appName;
    String packageName = packageInfo.packageName;
    String version = packageInfo.version;
    String buildNumber = packageInfo.buildNumber;

    print("appName:${appName}");

    print("packageName:${packageName}");

    print("version:${version}");

    print("buildNumber:${buildNumber}");
  }

//Get path
  _getAppPath() async {
    Directory tempDir = await getTemporaryDirectory();
    String tempPath = tempDir.path;

    Directory appDocDir = await getApplicationDocumentsDirectory();
    String appDocPath = appDocDir.path;

    var directory = await getExternalStorageDirectory();

    String storageDirectory = directory.path;

    print("tempPath:${tempPath}");

    print("appDocDir:${appDocPath}");

    print("StorageDirectory:${storageDirectory}");
  }

  //Download open file
  _downLoad() async {
    final directory = await getExternalStorageDirectory();
    String _localPath = directory.path;

    final taskId = await FlutterDownloader.enqueue(
      url: "http://www.ionic.wang/jdshop.apk",
      savedDir: _localPath,
      showNotification:
          true, // show download progress in status bar (for Android)
      openFileFromNotification:
          true, // click on notification to open downloaded file (for Android)
    );

    FlutterDownloader.registerCallback((id, status, progress) {
      print(status);
      // code to update your UI
      print('1111111');
      print(progress);
    });

    //Open file
    OpenFile.open("${_localPath}/jdshop.apk");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.arrow_downward),
        onPressed: _downLoad,
      ),
      appBar: AppBar(
        title: Text("app Upgrade demo"),
      ),
      body: Text("app Upgrade demo"),
    );
  }
}

Call URL_ The launcher module opens the external browser, opens the external application, dials the phone and sends text messages

1,Flutter url_launcher module

Flutter url_ The launcher module allows us to open external browsers, open external applications, send text messages, make calls and other functions.

https://pub.dev/packages/url_launcher

2,Flutter url_ Use of launcher module

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

class _UrlLauncherState extends State<UrlLauncher> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('UrlLauncher'),
      ),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(20),
          child: ListView(
            children: <Widget>[
              RaisedButton(
                child: Text('Open external browser'),
                onPressed: () async {
                  const url = 'https://cflutter.com';
                  if (await canLaunch(url)) {
                    await launch(url);
                  } else {
                    throw 'Could not launch $url';
                  }
                },
              ),
              SizedBox(
                height: 10,
              ),
              RaisedButton(
                child: Text('Make a call'),
                onPressed: () async {
                  const tel = 'tel:10086';
                  if (await canLaunch(tel)) {
                    await launch(tel);
                  } else {
                    throw 'Could not launch $tel';
                  }
                },
              ),
              SizedBox(height: 10),
              RaisedButton(
                child: Text('Send SMS'),
                onPressed: () async {
                  const tel = 'sms:10086';
                  if (await canLaunch(tel)) {
                    await launch(tel);
                  } else {
                    throw 'Could not launch $tel';
                  }
                },
              ),
              SizedBox(height: 10),
              RaisedButton(
                child: Text('Open external application'),
                onPressed: () async {
                  // weixin://
                  const app = 'alipays://';
                  if (await canLaunch(app)) {
                    await launch(app);
                  } else {
                    throw 'Could not launch $app';
                  }
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

3. Please refer to this post for opening other app s

https://www.cflutter.com/topic/5d0853733b57e317a4d0af01

Android modifies the application name, application icon and application startup screen

1. Android modify app name

android is in android ▸ app ▸ src ▸ main ▸ androidmanifest Modify android:label = "XXX" in XML;

2. Android modify app Icon

android replaces the corresponding image in the corresponding folder under android ▸ app ▸ src ▸ res ▸ mipmap

3. Android modify application startup screen

Android add startup interface

Open the file android/app/src/main/res/drawable/launch_background.xml

Modify the content and open the annotated code launch_ That part.

<!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->

IC inside_ launch. PNG is an icon, and launch is added to the startup screen_ image. Png, format requirements png

Note @ mipmap/launch_image is the name of the image resource of the startup interface you want to set. You need to put it in the corresponding folder

densityRepresentative resolution
ldpi240 x 320
mdpi320 x 480
hdpi480 x 800
xhdpi720 x 1280
xxhdpi1080 x 1920
xxxhdpi3840×2160

The vertical ListView is nested with the horizontal ListView, and the ListView is nested with the GridView

1. Vertical ListView nested horizontal ListView precautions:

When nesting the horizontal ListView in the vertical ListView, pay attention to add a Container to the outer layer of the horizontal ListView, and then set the height of the outer Container. The outer Container can be SizedBox or Container.

2. Precautions for nesting listgridview:

Since both GridView and ListView are scrollable components, when nesting, pay attention to changing the components inside into non scrollable components.

Important attributes:

shrinkWrap: true, / / solve the problem of infinite height

physics:NeverScrollableScrollPhysics(), / / disable sliding events

Adaptation of different terminal screens

The code I wrote still uses Adapter???

JSON serialization and deserialization (model class)

1. Manually serialize JSON using dart:convert

2. Serialize JSON in model class

Using dart:convert to manually serialize JSON in small projects is very good and fast. However, as the project grows, dart:convert manually serializes JSON and loses most of the static typing language features: type safety, automatic completion and the most important compile time exceptions. In this way, our code may become very error prone.

When we access the name or email field, we enter it quickly, resulting in the wrong field name. However, because the JSON is in the map structure, the compiler does not know the wrong field name.

In order to solve the above problems, we use more JSON serialization in model classes in large projects.

JSON string and Map type conversion dart:convert manually serialize JSON

import 'dart:convert';

    var mapData = {"name": "Zhang San", "age": "20"};
    var strData = '{"name":"Zhang San","age":"20"}';
    print(json.encode(mapData)); //Convert Map to Json string
    print(json.decode(strData)); //Convert Json string to Map type

Serialize JSON in model class

class FocusModel {
  String sId;
  String title;
  String status;
  String pic;
  String url;

  FocusModel({this.sId, this.title, this.status, this.pic, this.url});

  FocusModel.fromJson(Map<String, dynamic> json) {
    sId = json['_id'];
    title = json['title'];
    status = json['status'];
    pic = json['pic'];
    url = json['url'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['_id'] = this.sId;
    data['title'] = this.title;
    data['status'] = this.status;
    data['pic'] = this.pic;
    data['url'] = this.url;
    return data;
  }
}
var strData='{"_id":"59f6ef443ce1fb0fb02c7a43","title":"Notebook computer","status":"1","pic":"public\\upload\\UObZahqPYzFvx_C9CQjU8KiX.png","url":"12"}';

var data=FocusModel.fromJson(strData);

Refer to: https://flutterchina.club/json/

json_to_dart automatically generates model classes

https://javiercbk.github.io/json_to_dart/

IndexedStack keeps the page state

IndexedStack, like Stack, is a layer layout control. You can place another control on top of one control, but the only difference is that IndexedStack can only display one of the child controls at the same time, and the displayed controls can be set through the Index property.

The advantage of IndexedStack to keep the page state is simple configuration. The disadvantage of IndexedStack maintaining page state is that it is not convenient to control the state of each page separately.

body: IndexedStack(
        index: this._currentIndex,
        children: <Widget>[],
      ),

AutomaticKeepAliveClientMixin keep page state

garish...

Open sidebar by event

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
return Scaffold( key:_scaffoldKey,
appBar: AppBar(
title: Text("Product list"),
) )
Expanded(
flex: 1,
child: InkWell( onTap: () {
_scaffoldKey.currentState.openEndDrawer(); },
child: Text("screen", textAlign: TextAlign.center), ),
)

Modify theme style

return MaterialApp(
debugShowCheckedModeBanner: false, // home: Tabs(),
initialRoute: '/', onGenerateRoute:onGenerateRoute, theme: ThemeData(
primaryColor: Colors.white ),
);

Drop down menu showMenu

IconButton(
              icon: Icon(Icons.more_horiz),
              onPressed: () {
                showMenu(
                    context: context,
                    position: RelativeRect.fromLTRB(500, 76, 10, 0),
                    items: [
                      PopupMenuItem(
                        child: Row(
                          children: <Widget>[
                            Icon(Icons.home),
                            Container(
                              padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
                              child: Text("home page"),
                            )
                          ],
                        ),
                      ),
                      PopupMenuItem(
                        child: Row(
                          children: <Widget>[
                            Icon(Icons.search),
                            Container(
                              padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
                              child: Text("search"),
                            )
                          ],
                        ),
                      )
                    ]);
              })

Pop up bottom menu

In fact, after clicking the event, the showmodal bottomsheet pops up. Refer to the diolog notes

StatefulBuilder updates the state in the shutter, ShowDialog, showModalBottomSheet

reference resources: https://www.cflutter.com/topic/5d202202403aa10564178c65

State management

Generally speaking: when we want to share state (data) between multiple pages (components / widgets), or between multiple sub components in a page (components / widgets), we can use the state management in fluent to manage the unified state (data) and realize the direct value transmission and data sharing of different components.

At present, Flutter has many state management schemes, such as redux, bloc, state, provide and provider.

At present, we recommend using provider, which is an official state management solution. Compared with other state management libraries, it is more convenient to use.

provider library and shutter provide library

provider is the state management mode launched by the Flutter team.

The official address is: https://pub.dev/packages/provider

Note: provider and provide are two libraries. The official recommendation of Flutter is to use the provider. Oh, the provider is the official recommendation of Flutter. Provide wasn't officially written by Flutter.

Use of provider

(the official document shall prevail, and the builder keyword changes to creat e)

1. Configuration dependency provider: ^4.3.3
2. Create a new folder called provider, and put our state management class in the provider folder

3. Create a counter in the provider dart
4,Counter. Create a new class in dart to inherit the ChangeNotifier code of minxins as follows

import 'package:flutter/material.dart';

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

5. Find main Dart modification code is as follows

import 'package:flutter/material.dart';
import 'routers/router.dart';
import 'package:provider/provider.dart';
import 'provider/Counter.dart';

void main() => runApp(MyApp());

// void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          // Provider<Counter>.value(value: foo),
             ChangeNotifierProvider(create: (_) => Counter()), // provider4. Writing method of X global listening
          //ChangeNotifierProvider(builder: (_) => Counter()),
        ],
        child: MaterialApp(
          // home: Tabs(),
          debugShowCheckedModeBanner: false,
          initialRoute: '/productContent',
          onGenerateRoute: onGenerateRoute,
          theme: ThemeData(
              // primaryColor: Colors.yellow
              primaryColor: Colors.white),
        ));
  }
}

6. Get value, and set value

import 'package:provider/provider.dart';
import '../../provider/Counter.dart';

Widget build(BuildContext context) {
  final counter = Provider.of<Counter>(context); // counter.init();// In build
  return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          counter.increment();
        },
      ),
      body: Text("counter Value of:${counter.count}"));
}

Extended chestnut

import 'package:flutter/material.dart';

class Cart with ChangeNotifier {
  List _cartList = []; //state
  // int _cartNum=0; // The quantity is directly obtained from the array without defining a method to obtain the quantity

  int get cartNum => this._cartList.length; //The quantity is directly obtained from the array without defining a method to obtain the quantity

  List get cartList => this._cartList;

  addData(value) {
    this._cartList.add(value);
    notifyListeners();
  }

  deleteData(value) {
    this._cartList.remove(value);
    notifyListeners();
  }
}

event_bus event broadcast event listening

garish...

MediaQuery.removePadding removes the pandding of the element

Via mediaquery Removepadding can remove the padding of an element. Note that you need to specify which direction of padding to remove, such as removing the above padding

MediaQuery.removePadding(
            removeTop: true,
            context: context,
            child: ,
)

Waterfall flow layout

https://pub.dev/packages/flutter_staggered_grid_view

new StaggeredGridView.countBuilder(
  crossAxisCount: 4,
  itemCount: 8,
  itemBuilder: (BuildContext context, int index) => new Container(
      color: Colors.green,
      child: new Center(
        child: new CircleAvatar(
          backgroundColor: Colors.white,
          child: new Text('$index'),
        ),
      )),
  staggeredTileBuilder: (int index) =>
      new StaggeredTile.count(2, index.isEven ? 2 : 1), //Fixed number modify count() to fit(2)
  mainAxisSpacing: 4.0,
  crossAxisSpacing: 4.0,
)

Sliver awesome!!!

return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
//            title: Text("SliverAppBar"),
//            pinned: true,
            floating: true,
            expandedHeight: 200,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("Hello Flutter".toUpperCase()),
              background: Image.network(
                "https://images.unsplash.com/photo-1579964190836-13f5022f5c40?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
                fit: BoxFit.cover,
              ),
            ),
          ),
          SliverGrid(
              delegate: SliverChildBuilderDelegate((context, index) {
                return Container(
                  child: Center(
                    child: Text("$index"),
                  ),
                );
              }, childCount: 1000),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 8.0,
                  mainAxisSpacing: 8.0))
        ],
      ),
    );

Adapt to night mode

Simple approach:

Direct setting mode

theme: ThemeData.dark(),

Automatic switching mode

darkTheme: ThemeData.dark()

positive:

Dark Mode, also known as Dark Mode or Dark Mode, is a display mode with high contrast or reverse color mode. After it is turned on, it can alleviate fatigue at night, make it easier to read, and save power to a certain extent.

Night mode following system

Using the darkTheme option of MaterialApp, you can easily adapt the DarkMode of the following system:

MaterialApp( 
    theme: ThemeData( 
        brightness: Brightness.light, 
        primaryColor: Colors.blue, 
    ), 
    darkTheme: ThemeData( 
        brightness: Brightness.dark, 
    ),
);

It can also be written directly

darkTheme: ThemeData.dark()
  • This method is automatically switched according to the system settings of iOS/Android, and there is no need for the user to set it separately

Manually turn on night mode

The experience of the above-mentioned follow-up system automatically switching to dark mode may not be very good. For example, the user does not like the night mode or the color matching of the app's night mode is not very good, which will lead to the user unable to manually control the app's night mode or can only turn off the system settings. Therefore, we can add the options of manual control and follow-up system, so that users can choose whether to turn it on and how to turn it on.

Save user configuration

Shared can be used in fluent_ Preferences to save the user's configuration data. See shared for details_ Preferences use

State management

The manual switching of topics affects the overall situation, which is difficult to achieve through the conventional data flow. Common status management:

  • InheritedWidget
  • Scoped model
  • BLoC
  • Redux
  • Provider

Provider is the officially recommended state management method announced by Google I/O 2019 conference. We need to share the changed state with other widgets through user settings on the settings page. Here, provider is used to realize state sharing.

Universal night mode Provider Model class

import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';

class DarkModeModel with ChangeNotifier {
  ///Night mode 0: off 1: on 2: with the system
  int _darkMode;

  static const Map<int, String> darkModeMap = {
    0: "close",
    1: "open",
    2: "Follow system"
  };

  static const String STORE_KEY = 'darkMode';

  SharedPreferences _prefs;

  int get darkMode => _darkMode;

  DarkModeModel() {
    _init();
  }

  void _init() async {
    this._prefs = await SharedPreferences.getInstance();
    int localMode = this._prefs.getInt(STORE_KEY);
    changeMode(localMode ?? 0);
  }

  void changeMode(int darkMode) async {

    _darkMode = darkMode;

    notifyListeners();

    SharedPreferences prefs = this._prefs ?? SharedPreferences.getInstance();

    await prefs.setInt(STORE_KEY, darkMode);
  }
}

MaterialApp modification

If you manually control whether to turn on the night mode, you can set the theme option of MaterialApp to themedata dark()

theme: ThemeData.dark()

Because you need to keep both automatic switching and manual switching with the system at the same time, and there is a conflict between the darkTheme option and theme, you need to use the darkmodemodel The value of darkmode is used to render different materialapps. If it is in manual mode, it is based on darkmodemodel The value of darkmode is used to render different themes.

MultiProvider(
  providers: [
    ChangeNotifierProvider(builder: (_) => DarkModeModel())
  ],
  child: Consumer<DarkModeModel>(
    builder: (context, darkModeModel, _) {
      return darkModeModel.darkMode == 2 
        ? MaterialApp(
            title: 'Special effect King',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            darkTheme: ThemeData.dark(),
              home: MainPage(title: 'Special effect King'),
            ) 
        : MaterialApp(
            title: 'Special effect King',
            theme: darkModeModel.darkMode == 1 
              ? ThemeData.dark()
              : ThemeData(
                  primarySwatch: Colors.blue,
                ),
            home: MainPage(title: 'Special effect King'),
          );
    },
  ),
)

In this way, we can provide users with the options of automatic follow-up system switching and manual control

Login registration case

login.dart

class Login extends StatefulWidget {
  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  String _nickname = "";
  String _password = "";
  final _formKey = GlobalKey<FormState>();
  bool _autoValidate = false;

  _toLogin() async {
    Response response = await Dio().post("http://127.0.0.1:8080/auth",
        data: {"nickname": _nickname, "password": _password});
    LoginModel signUp = LoginModel.fromJson(response.data);

    if (signUp.code == 2000) {
      Scaffold.of(context).showSnackBar(SnackBar(content: Text(signUp.msg)));
    } else {
      Scaffold.of(context).showSnackBar(SnackBar(content: Text(signUp.msg)));
    }
  }

  void _submitForm() {
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      _toLogin();
    } else {
      setState(() {
        _autoValidate = true;
      });
    }
  }

  String _validateNickname(String value) {
    if (value.isEmpty) {
      return "<nickname>Cannot be empty!";
    } else if (value.length > 20) {
      return "<nickname>Cannot be greater than 20 characters!";
    }
    return null;
  }

  String _validatePassword(String value) {
    if (value.isEmpty) {
      return "<password>Cannot be empty!";
    } else if (value.length < 6) {
      return "<password>Cannot be less than 6 digits!";
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          FocusScope.of(context).requestFocus(FocusNode());
        },
        // The outer package has a Container, which is convenient to set the inner and outer margins, background pictures, etc
        child: Container(
          padding: EdgeInsets.all(40.0),
          child: ListView(
            children: <Widget>[
              // Login LOGO
              Container(
                height: 100,
                decoration: BoxDecoration(
                  image: DecorationImage(
                    image: NetworkImage(
                      "https://c-ssl.duitang.com/uploads/item/201607/23/20160723143004_vyjTa.thumb.1000_0.jpeg",
                    ),
                  ),
                ),
              ),

              // form 
              Form(
                key: _formKey,
                child: Column(
                  children: <Widget>[
                    // nickname
                    TextFormField(
                      decoration: InputDecoration(
                        labelText: "nickname",
                        hintText: "Please enter login nickname",
                        helperText: '',
                      ),
                      onSaved: (String value) {
                        _nickname = value;
                      },
                      autovalidate: _autoValidate,
                      validator: _validateNickname,
                    ),

                    // password
                    TextFormField(
                      decoration: InputDecoration(
                        labelText: "password",
                        hintText: "Please enter the login password",
                        helperText: '',
                      ),
                      obscureText: true,
                      onSaved: (String value) {
                        _password = value;
                      },
                      autovalidate: _autoValidate,
                      validator: _validatePassword,
                    ),

                    // Login button
                    Container(
                      child: RaisedButton(
                        child: Text("Sign in"),
                        onPressed: _submitForm,
                        color: Theme.of(context).accentColor,
                        elevation: 0.0,
                      ),
                    ),
                  ],
                ),
              ),

              // Register a new account
              Container(
                child: FlatButton(
                  onPressed: () {
                    Navigator.of(context).push(
                        MaterialPageRoute(builder: (context) => SignUp()));
                  },
                  child: Text("Register a new account"),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Shuttle sliderappbar hide / show navigation bar

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
          headerSliverBuilder: _sliverBuilder,
          body: Center(
            child: Text('hahaha'),
          )),
    );
  }
}
 
List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
  return <Widget>[
    SliverAppBar(
      centerTitle: true, //Title centered
      expandedHeight: 200.0, //Deployment height 200
      backgroundColor: Colors.tealAccent,
      floating: false, //Hide title without sliding
      pinned: false, //Not fixed on top
      flexibleSpace: FlexibleSpaceBar(
        centerTitle: true,
        background: Image.asset(
          "assets/pic.jpg", 
          fit: BoxFit.cover,
        ),
      ),
    )
  ];
}

Skeleton screen

https://pub.dev/packages/pk_skeleton

Full screen background image of shutter (including appbar and status bar)

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    SelfAdapt _adapt = SelfAdapt.init(context);
    return Container(
      width: _adapt.width,
      height: _adapt.height,
      decoration: BoxDecoration(
        image: DecorationImage(
          image: NetworkImage('https://img.zcool.cn/community/0372d195ac1cd55a8012062e3b16810.jpg'),
          fit: BoxFit.cover,
        ),
      ),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        appBar: AppBar(
          elevation: 0,
          backgroundColor: Colors.transparent,
          // title: Text('home page '),
        ),
        drawer: MyDrawer(),
        body: Container(
          width: _adapt.width,
          padding: _adapt.setfromLTRB(100, 0, 100, 0),
          child: Text('hello'),
        ),
      ),
    );
  }
}

Aurora push:

Register an account ----- create an application ----- appkey ----- the application registration should be consistent

integrate

1. Download dependency

Android:
stay /android/app/build.gradle Add the following code to:

android: {
  ....
  defaultConfig {
    applicationId "Replace with your own application ID"
    ...
    ndk {
	//Select the corresponding cpu type to add so library.
	abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64', 'mips', 'mips64', 'arm64-v8a',        
    }

    manifestPlaceholders = [
        JPUSH_PKGNAME : applicationId,
        JPUSH_APPKEY : "appkey", // Note: Appkey corresponding to the package name registered on jpush
        JPUSH_CHANNEL : "developer-default", //Fill in the default value temporarily
    ]
  }    
}
 void initState() {
    super.initState();
    this.initJpush();

    // ///Create JPush
    // JPush jpush = new JPush();

    // ///Configure application Key
    // jpush.setup(
    //   appKey: "3cc9670e26b5b3e83cabe979",
    //   channel: "theChannel",
    //   production: false,

    //   ///Set whether to print debug log
    //   debug: true,
    // );
  }

Or init:

 void initState() {
    // TODO: implement initState
    super.initState();
    this.initJpush();
  }

  //Monitor Aurora push (custom method)
  //https://github.com/jpush/jpush-flutter-plugin/blob/master/documents/APIs.md
  initJpush() async {
    JPush jpush = new JPush();
    //Get registered id
    jpush.getRegistrationID().then((rid) {
      print("Get registered id:$rid");
    });
    //initialization
    jpush.setup(
      appKey: "17d78ecf32c322db169a1d98",
      channel: "theChannel",
      production: false,
      debug: true, // Set whether to print debug log
    );

    //Set alias to push the specified user
    jpush.setAlias("jg123").then((map) {
      print("Successfully set alias");
    });

    try {
      //Monitor message notification
      jpush.addEventHandler(
        // Receive notification callback method.
        onReceiveNotification: (Map<String, dynamic> message) async {
          print("flutter onReceiveNotification: $message");
        },
        // Click the notification callback method.
        onOpenNotification: (Map<String, dynamic> message) async {
          print("flutter onOpenNotification: $message");
        },
        // Receive custom message callback method.
        onReceiveMessage: (Map<String, dynamic> message) async {
          print("flutter onReceiveMessage: $message");
        },
      );
    } catch (e) {
      print('aurora sdk Configuration exception');
    }
  }

Specify device push

sockio

var http=require('http');

var fs=require('fs');  /*fs Built in module*/

var app=http.createServer(function(req,res){
    //Load static page
    fs.readFile('app.html',function(err,data){

        res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});
        res.end(data);
    })
})

//Introduce socket io
var io = require('socket.io')(app);

io.on('connection', function (socket) {

    console.log('The server has established a connection');

    //The server obtains the data broadcast by the client

    socket.on('addcart',function(data){

        console.log(data);

        //The server sends data to the client

        //socket.emit();   /* Who sends me a message? I broadcast the message to who*/

        //io.emit() ;   /* Broadcast data to all clients connected to the server*/


        //socket. Emit ('To client ',' I am the data of the server '+ data.client);

        io.emit('to-client','I am the data of the server'+data.client)

    })

});




app.listen(3000);


/*Use socket io
1.install

 npm install socket.io

 2,Import establish connection

 var io = require('socket.io')(app);


 io.on('connection', function (socket) {

    console.log('The server has established a connection ');
 });

3,Introduce js into client html

 http://localhost:3000/socket.io/socket.io.js





* */

fingerprint

 android:theme="@style/Theme.AppCompat"

package com.xinxing.luckly_flutter

//import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
}

Some configurations using biometric authentication in FlutterIos
Info. Add the following configuration to plist

NSFaceIDUsageDescriptionfaceid for authentication?

Keywords: Programming Interview Flutter dart

Added by simpli on Tue, 08 Feb 2022 02:03:28 +0200