The past and present life of this article

I got this article out of my draft box, and it’s a little bit of history, but let me tell you a little bit about my history with Flutter.

The first contact with Flutter was at the beginning of 2019. At that time, the company’s product App was developed using the framework of Uni-app. At that time, the users of Uni-app were relatively small, the community was not perfect, and the nature of Uni-app itself was web, which led to some performance problems in the long list of apps. For those of you who didn’t do Android and IOS development, my Leader and I later did some technical research, and we turned our attention to the mobile cross-end framework dark horse Flutter. Later, we resolutely used The Flutter to re-develop the App. At that time, THE Leader and I were not used to the first contact with Flutter. However, we had no way out when we were ordered in a crisis. It took us nearly two weeks from writing the first line of DART code to completing the development of the entire App. Two days in front of the familiar with new code style and development mode, slowly to fit into the ball, often develop late into the night, with two weeks to quickly learn a new framework as well as the actual combat, this two weeks and I feel the most fulfilling the year time, practice is the only way to truth, is also the most effective way to (salute those who struggle of years).

Since the launch of the revitalized App, performance and user experience have been greatly improved.

What are the antecedents of this passage?

Since then, various aspects of the App have been optimized, and some new apps have been developed with Flutter, and the understanding of Flutter has been deepened. Until I moved to a new company, I didn’t use Flutter much, so I felt a little rusty. One day, I suddenly remembered it, and my face turned red. I felt like I was thinking of my ex-girlfriend, so I wrote a Music App on my whim. During some sudden want to record down the idea, there is this article, yes, this is a miscellany of thoughts, no system, think of where to say. Therefore, it has not been sent out before, and now I think that knowledge is used to share, if it solves the problem that is bothering some users, it may be the best destination for this article, at least to see the light of day.

The consequences?

This article may be described in some places will be relatively simple, the author will try to warm up and continue to update and optimize this miscellaneous book!!

What wrong place welcome big guy to testify, really haven’t written Flutter for a long time!

Start the text!

Some tips on kinetic effects

The animation value is 0-1

The majority of the effect (prevent the face), the process is from 0 to 1, this is a bit abstract, most Wudang disciples do not understand that tai Chi produces two yi, two yi produces four xiang, four xiang produces eight diagrams (how the eight diagrams evolve I do not know)… The true meaning of that Zhang Sanfeng and others can understand the essence of it, the achievement of the master, just around the corner. Digress…

For example:

  • 0 goes to 600 is 0 minus 1, right? // 0*600-1*600

Little xia?

get?

Just touch it…

1. Initialize the function

class Demo extends StatelessWidget{
    Demo(){
        doSomething()
    }
}


class Demo extends StatefulWidget{
    @override
    _DemoState createState = > _DemoState();
}
class _DemoState extends extends State<Demo>{
    _DemoState(){
        print('_DemoState first ');
        doSomething();
    }
    
    @override
    void initState(){
        print('initState first ');
        super.initState();
    }
    
    / / the result
    // _DemoState is output first
    // initState first
}
Copy the code

2. Use Model as much as possible

  • Using model parameters can detect errors in the program when the program is compiled.
class DemoModel{
    final Color color;
    final String title;
    DemoModel(this.color,this.title);
}

class Demo extends StatelessWidget{
    final DemoModel model;
    Demo({this.model})
}

Copy the code

3.ClipOval component to make different layers of pictures in the same layer display effect, look at the section of code

// Use Stack to place the image in the hierarchy

Stack:[
    Picture1(),
    Picture2()
]

// This will only show Picture2.Stack: [Picture1 (), ClipOval (clipper: CircleRevealClipper (parameters), the child: Picture2 ()))class CircleRevealClipper extends CustomClipper<Rect>{
    final double param;
    CircleRevealClipper(this.param);
    
    @override
    Rect getClip(Size size) {
        // Here draw the cut shape (route)
    }
    
    @override
    @override
    bool shouldReclip(CustomClipper<Rect> oldClipper) {
        // TODO: implement shouldReclip
        return true; }}Picture1 and Picture2 are displayed at the same time.


Copy the code

4. Usually fixed values are accepted with all capital English variables

// Good at reading
final BUBBLE_WIDTH = 55.0;
Copy the code

5. Use Transform to make simple animations

varTranslation = value; Transform( transform: Matrix4.translationValues(translation,0.0.0.0), the child: Widget ()) with AnimationController completionAnimationController =newAnimationController( duration: duration, vsync: vsync, ) .. addListener(() {print(completionAnimationController.value);
    
    / / completionAnimationController. The value value of 0-1
}
Copy the code

6. How to use enumeration values

enum SlideDirection { leftToRight, rightToLeft, none }

SlideDirection direction = SlideDirection.leftToright;
Copy the code

7. The process from transparency to display can use the Opacity component, which is also a 0-1 process.

8. One of the ways to implement top-level gesture events

Stack:[
    page(),
    GestureDetector(
      onHorizontalDragStart: onDragStart, // Touch the screen
      onHorizontalDragUpdate: onDragUpdate, // Touch the screen and swipe
      onHorizontalDragEnd: onDragEnd, // Leave the screen
    );
]
Copy the code

9. About the basic use of classes

class Demo{
    final double value;
    Demo({
        this.value
    }){
        // Initialize the function
        print('execution');
    }
    
    fn1(){
        / / the method body
    }
    
    fn2(){
        / / the method body}}/ / use

Demo a = new Demo();

a.fn1();
a.fn2();

Copy the code

10. About using StreamController

class Demo{
    final double value;
    Demo(this.value);
}
class Example extends StatefulWidget{
    @override
    _ExampleState createState => _ExampleState();
}
class _ExampleState extends State<Example>{
    StreamController<Demo> slideUpdateStream;
    _ExampleState(){
        slideUpdateStream = new StreamController<Demo>();
        // Set the listener
        slideUpdateStream.stream.listen((Demo d){
            // doSomething...}}})/ / triggers
class Trigger extends StatelessWidget{
    StreamController<Demo> demo;
    Trigger(){
        / / triggers
        demo.add(new Demo())
    }
}
Copy the code

11. Other small uses of SizeBox

Spacing between elements (some of which are flexible) can be implemented using SizeBox to avoid nesting again.

  • There is a spacing of 20.0 between the two elements
Row(
    children:[
        A(),
        SizeBox(
            width:20.0
        ),
        B()
        ...
    ]
)
Copy the code

12. On the use of position.fill () in a Stack

Position.fill() will fill the Stack

Stack(
    children:[
        Text("Try to stop me, boy?"),
        Position.fill(
            }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
            child:Text('I'm young, but my dad can't see the sun.')))Copy the code

13.PageView A very handy list of scrolling views (a simple version of the playround)

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example>{
   var currentPage = 1.0;
   PageController controller = PageController(initialPage: 0);
   
   @override
   void initState() {
    // TODO: implement initState
    controller.addListener(() {
      // Note that the controller. Page value here is what I call the process value, not the result value, such as from page 0 to page 1, or from 0.000to 0.99 to 1.0
      // And when the slide does not go to the next page, the value will bounce back (0.1-0.3-0.45-0.2-0.0). This feature can be used to achieve a rebound animation effect.
      setState(() {
        currentPage = controller.page;
      });
    });
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // For more information, please refer to Baidu
        body:PageView.builder(
            itemCount: images.length,
            controller: controller,
            reverse: true,
            itemBuilder: (context, index) {
                returnContain( ... ) }))}Copy the code

14. The use of FittedBox

The FittedBox will scale and position the child within its size range so that the child fits its size. The Fit property of the FittedBox is a bit like the background-size property used when writing CSS (it feels like children are being laid out as images).

Container(
        color: Colors.amberAccent,
        width: 300.0,
        height: 300.0,
        child: new FittedBox(
          fit: BoxFit.contain,
          alignment: Alignment.topLeft,
          child: new Container(
            color: Colors.red,
            child: new Text("FittedBox"),),),),Copy the code

15. The use of AspectRatio

AspectRatio will first expand as much as the layout constraints allow. The height of the widget is determined by the width and ratio. The height and width values are roughly the same as the code below

if{height = width / _aspectRatio; width = constraints.maxWidth; }else if(height limit) {height = constraints. MaxHeight; width = height * _aspectRatio; }else{
    height = constraints.maxHeight;
    width = constraints.maxWidth;
}
Copy the code

Note: The aspectRatio attribute cannot be empty

AspectRatio is used for views that require a fixed ratio between the height and width.

16. The use of ConstrainedBox

It can be simply understood as a container with limits to height and width, such as setting maximum height, minimum height, maximum width, minimum width. Look at the code:

ConstrainedBox(
    constraints: const BoxConstraints(
        minWidth: 220.0,
        minHeight: 100.0,
        maxWidth: 250.0,
        maxHeight: 150.0,
    ),
    child: new Container(
        width: 300.0,
        height: 200.0,
        color: Colors.red,
    ),
);
Copy the code

The final displayed child is 200 in height, 250 in width, and just so.

17.RepaintBoundary component can achieve screenshot effect

Use RepaintBoundary to wrap the part that you want to cut (controlled by key). RenderRepaintBoundary takes out the part that RepaintBoundary wraps and converts it into ui.image object by.toimage () method. The image is then converted toByteData using.tobytedata (). Finally, it is stored as File objects via File(‘ path ‘).writeasBytes (byteData). Look at the code:

GlobalKey rootWidgetKey = GlobalKey();
List<Uint8List> images = List(a); _interceptPng()async {
    try {
      RenderRepaintBoundary boundary = rootWidgetKey.currentContext.findRenderObject();
      var image = await boundary.toImage(pixelRatio: 3.0);
      ByteData byteData = await image.toByteData(format: ImageByteFormat.png);
      Uint8List pngBytes = byteData.buffer.asUint8List();
      images = [pngBytes];
      setState((){})
      // Or save the picture
      // File(' path ').writeasBytes (pngBytes);
    } catch (e) {
      print(e); }}@override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('RepaintBoundary Demo'),
        ),
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.camera),
          tooltip: 'capture',
          onPressed: () async {
            await this._interceptPng();
          },
        ),
        body: Column(
          children: <Widget>[
            RepaintBoundary(
                key: rootWidgetKey, 
                child: Container(
                    height:100.0,
                    width:30.0,
                    color:Colors.green
                )
            ),
             Expanded(
               child: ListView.builder(
                 itemBuilder: (context, index) {
                   return Image.memory(
                     images[index],
                     width: 100.0,
                     height: 200.0,); }, itemCount: images.length, scrollDirection: Axis.vertical, ), ) ], ), ), ) }Copy the code

18. Use of the CircleAvatar component

If you look at the meaning of the English words, you can see that you can use this component to achieve the effect of a round head. However, you need to set the backgroundImage or backgroundColor property to achieve the circular effect. The Child property can be thought of as setting the Widgets on top of the avatar. Look at the code:

CircleAvatar(
    radius: 40.0,
    backgroundColor: Colors.red,
    child: Text("test"),Copy the code

Use of the IndexedStack component

The IndexedStack inherits from the Stack and is used to display the index child component. The other child components are not visible, but the state of all components is preserved. (You can also do this with the OffStage component, but they are expensive. All child components are instantiated during page load initialization.

IndexedStack(
    index: currentIndex, // The index displayed
    children: widgetList, // Subcomponent list
)
Copy the code

20. AutomaticKeepAliveClientMixin class

WantKeepAlive: wantKeepAlive: wantKeepAlive: wantKeepAlive: wantKeepAlive: true;

class TestPage extends StatefulWidget {
 @override
 _TestPageState createState() => _TestPageState();
}
 
class _TestPageState extends State<TestPage> with AutomaticKeepAliveClientMixin {
 
 // Overwrite properties can be generated directly by the plugin via error repair prompt, and the corresponding value can be changed
 @override
 bool get wantKeepAlive => true;
 
 @override
 void initState() {
    super.initState();
 }
 
 @override
 Widget build(BuildContext context) {
  ...
}
Copy the code

21.InheritedWidget

Updating…