[toc]

Flutter from entry to collapse (II) : A personal central interface

Remember before

Above we have a login interface, because it is very simple, and is at the entry level. And then I found out I was poisoned… This layout is fun to write… I’m starting to despise XML…

Answers to questions left over from the previous post

In my last article, I joked that the listView sliding inside Flutter would be a bit slow. Then I went to the development group and asked the big guy. The final solution is: what is longitudinal silking when releasing the package

The specific reason may be that compilation and release package are usually used in different ways, so it will lead to different effects. Specific use of the feeling more smooth than WEEX! I’m satisfied with that.

Personal-centric interface implementation

Results show

Page and dismantling

CustomScrollView

CustomScrollView is a very powerful control, powerful to I will only a little fur, the most pit is not found online what more comprehensive information, the document is in English… And my understanding of him is that it can realize the android android. Support. The design. The widget. The CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout + recyclerView effect.

CustomScrollView introduction

CustomScrollView CustomScrollView CustomScrollView

First take a look at the source code:

/// See also:
///
/// * [SliverList], which isa sliver that displays linear list of children.
/// * [SliverFixedExtentList], which isa more efficient sliver that displays
/// linear list of children that havethe same extent along the scroll axis.
/// * [SliverGrid], which isa sliver that displays a 2D array of children.
/// * [SliverPadding], which isa sliver that adds blank space around another
/// sliver.
/// * [SliverAppBar], which isa sliver that displays a header that can expand
/// and float asthe scroll view scrolls.
/// * [ScrollNotification] and [NotificationListener], which can be used to watch
///    the scroll position without using a [ScrollController].
class CustomScrollView extends ScrollView {
  /// Creates a [ScrollView] that creates custom scroll effects using slivers.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
  CustomScrollView({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap: false.this.slivers: const <Widget>[],
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
    controller: controller,
    primary: primary,
    physics: physics,
    shrinkWrap: shrinkWrap,
  );
Copy the code

As you can see, the main things we can use are:

  1. SliverAppBar (similar to CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout)
  2. SliverGridv (similar to RecyClerView or GrideView)
  3. SliverFixedExtentList(similar to RecyClerView or ListView)

Let’s run the official demo code, and the effect and code are as follows:

Widget showCustomScrollView() {
  return new CustomScrollView(
    slivers: <Widget>[
      const SliverAppBar(
        pinned: true,
        expandedHeight: 250.0,
        flexibleSpace: const FlexibleSpaceBar(
          title: const Text('Demo'),),),new SliverGrid(
        gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 200.0,
          mainAxisSpacing: 10.0,
          crossAxisSpacing: 10.0,
          childAspectRatio: 4.0,
        ),
        delegate: new SliverChildBuilderDelegate(
              (BuildContext context, int index) {
            return new Container(
              alignment: Alignment.center,
              color: Colors.teal[100 * (index % 9)],
              child: new Text('grid item $index')); }, childCount:20,),),new SliverFixedExtentList(
        itemExtent: 50.0,
        delegate: new SliverChildBuilderDelegate(
              (BuildContext context, int index) {
            return new Container(
              alignment: Alignment.center,
              color: Colors.lightBlue[100 * (index % 9)],
              child: new Text('list item $index')); },),),],); }Copy the code

It is very simple to see the corresponding properties of each Widget, Android students can really understand: AppBar,RecyclerView to deepen understanding.

CustomScrollView use

The page effect is:

The main code is:

 return new CustomScrollView(reverse: false, shrinkWrap: false, slivers: <
        Widget>[
      new SliverAppBar(
        pinned: false,
        backgroundColor: Colors.green,
        expandedHeight: 200.0,
        iconTheme: new IconThemeData(color: Colors.transparent),
        flexibleSpace: new InkWell(
            onTap: () {
              userAvatar == null ? debugPrint('login') : debugPrint('User Information');
            },
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                userAvatar == null
                    ? new Image.asset(
                        "images/ic_avatar_default.png",
                        width: 60.0,
                        height: 60.0,) :new Container(
                        width: 60.0,
                        height: 60.0,
                        decoration: new BoxDecoration(
                            shape: BoxShape.circle,
                            color: Colors.transparent,
                            image: new DecorationImage(
                                image: new NetworkImage(userAvatar),
                                fit: BoxFit.cover),
                            border: new Border.all(
                                color: Colors.white, width: 2.0))),new Container(
                  margin: const EdgeInsets.fromLTRB(0.0.10.0.0.0.0.0),
                  child: new Text(
                    userName == null ? 'Click your avatar to log in' : userName,
                    style: new TextStyle(color: Colors.white, fontSize: 16.0() (() [() (() () (new SliverFixedExtentList(
          delegate:
              new SliverChildBuilderDelegate((BuildContext context, int index) {
            String title = titles[index];
            return new Container(
                alignment: Alignment.centerLeft,
                child: new InkWell(
                  onTap: () {
                    print("the is the item of $title");
                  },
                  child: new Column(
                    children: <Widget>[
                      new Padding(
                        padding:
                            const EdgeInsets.fromLTRB(15.0.15.0.15.0.15.0),
                        child: new Row(
                          children: <Widget>[
                            new Expanded(
                                child: new Text(
                              title,
                              style: titleTextStyle,
                            )),
                            rightArrowIcon
                          ],
                        ),
                      ),
                      new Divider(
                        height: 1.0() [(), ()). }, childCount: titles.length), itemExtent:50.0),]);Copy the code

The code looks redundant…

But when it comes to folding layouts with XML, it seems pretty neat…

Vernacular time
  1. The entire view has only one root layout, i.eCustomScrollView, the following are implemented respectivelySliverAppBar(the header used to implement the personal center) as wellSliverFixedExtentList(Used to implement the item below the header)
  2. SliverAppBarThe background color is forgive green, the height of the expansion is 200.0, and it is not fixed, it belongs to the foldable, its item is transparent (actually located in the top left corner, but transparent, and there is no click event); Its child layout is a vertical linear layout (23333) :
    1. The first layout is a head: a head? Show profile picture: Display the placeholder map
    2. The second layout is a user nickname: have a nickname? Show nickname: Show ‘Click to log in’
  3. SliverFixedExtentListContains a list, each item is 50.0 in height, and its delegate isSliverChildBuilderDelegate, its childer is oneContainer
    1. The Container is left-aligned, and its children wrap oneInkWellTo provide click events
    2. InkWell contains a vertically aligned linear layout (233333) that contains a horizontal linear layout (23333) Row and a dividing line
      1. The Row contains the item text on the left and the arrow on the right
      2. The Divider provides a line with a height of 1.0
CustomScrollView summary

The entire personal center is implemented using a CustomScrollView, but this is just one of its functions. As a control with a lot of potential, it has many uses to explore.

ListView multiple layouts:

ListView to introduce

I believe that whether Android dog or ios Wang, for listView is quite familiar, WHEN I was still in school, the teacher said a word: do not look down on the adapter, I bet you must be dealing with adapters every day. While listview&GridView & RecyclerView… Must be better than the hand, here do not teach fish to swim.

Code implementation

In fact, when writing listView will always compare to think of android writing method, I think this kind of idea is actually quite good, can compare to strengthen memory, such as the following I put them apart:

  1. oncreateViewHolder+getItemCount
  2. onBindViewHolder
OncreateViewHolder +getItemCount(returns itemBuild and count)

This is the widget that returns the item and the count that returns the item, but that’s not the point

  @override
  Widget build(BuildContext context) {
// return showCustomScrollView();
// Return the listView we built. Remember that count is twice the value of the data source
    var listView = new ListView.builder(
      itemBuilder: (context, i) => renderRow(context,i),
      itemCount:   titles.length * 2,);returnlistView; ###### onBindViewHolder (widget that generates each item) dart renderRow(context, I) {final userHeaderHeight = 200.0;
    if (i == 0) {
      var userHeader = new Container(
          height: userHeaderHeight,
          color: Colors.green,
          child: new Center(
              child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              userAvatar == null
                  ? new Image.asset(
                      "images/ic_avatar_default.png",
                      width: 60.0,) :new Container(
                      width: 60.0,
                      height: 60.0,
                      decoration: new BoxDecoration(
                          shape: BoxShape.circle,
                          color: Colors.transparent,
                          image: new DecorationImage(
                              image: new NetworkImage(userAvatar),
                              fit: BoxFit.cover),
                          border:
                              new Border.all(color: Colors.white, width: 2.0))),new Container(
                margin: const EdgeInsets.fromLTRB(0.0.10.0.0.0.0.0),
                child: new Text(
                  userName == null ? 'Click your avatar to log in' : userName,
                  style: new TextStyle(color: Colors.white, fontSize: 16.0),),),)));return new GestureDetector(
        onTap: () {
          Navigator.push(context,
              new MaterialPageRoute(builder: (context) => new LoginPage()));
        },
        child: userHeader,
      );
    }
    --i;
    if (i.isOdd) {
      return new Divider(
        height: 1.0,); } i = i ~/2;
    String title = titles[i];
    var listItemContent = new Padding(
      padding: const EdgeInsets.fromLTRB(10.0.15.0.10.0.15.0),
      child: new Row(
        children: <Widget>[
          new Expanded(
              child: new Text(
            title,
            style: titleTextStyle,
          )),
          rightArrowIcon
        ],
      ),
    );
    return newInkWell( child: listItemContent, onTap: () {}, ); }} because the layout of the item is very simple, the renderRow is also quite simple. In general, the layout is similar to the CustomScrollView layout above, so I won't go into details here. Perhaps more notable is the multi-item approach:1.When the item is0", return the profile picture information2.For even numbers, return a dividing line3.If it's odd, it returns the true number of items and that's why itemCount is true *, right2: * A header + real number + real number- 1Dividing line = actual number *2*. Because it's more... and1.Too much, a little dizzy, not very careful writing, I posted the entire DART file, the code is in it: dartimport 'package:flutter/material.dart';
import 'login/LoginPage.dart';

class MyInfoPage extends StatelessWidget {
  static const double IMAGE_ICON_WIDTH = 30.0;
  static const double ARROW_ICON_WIDTH = 16.0;

  var userAvatar;
  var userName;
  var titles = ["My message"."Reading records"."My Blog"."My Question and Answer"."My Activities"."My team"."Invite friends"];
  var imagePaths = [
    "images/ic_my_message.png"."images/ic_my_blog.png"."images/ic_my_blog.png"."images/ic_my_question.png"."images/ic_discover_pos.png"."images/ic_my_team.png"."images/ic_my_recommend.png"
  ];

  var titleTextStyle = new TextStyle(fontSize: 16.0);
  var rightArrowIcon = new Image.asset(
    'images/ic_arrow_right.png',
    width: ARROW_ICON_WIDTH,
    height: ARROW_ICON_WIDTH,
  );

  @override
  Widget build(BuildContext context) {
// return showCustomScrollView();
    var listView = new ListView.builder(
      itemBuilder: (context, i) => renderRow(context,i),
      itemCount:   titles.length * 2,);return listView;
// return new CustomScrollView(reverse: false, shrinkWrap: false, slivers: <
// Widget>[
// new SliverAppBar(
// pinned: false,
// backgroundColor: Colors.green,
/ / expandedHeight: 200.0.
// iconTheme: new IconThemeData(color: Colors.transparent),
// flexibleSpace: new InkWell(
// onTap: () {
// userAvatar == null ? DebugPrint (' Login ') : debugPrint(' user information ');
/ /},
// child: new Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: 
      
       [
      
// userAvatar == null
/ /? new Image.asset(
// "images/ic_avatar_default.png",
/ / width: 60.0.
/ / height: 60.0.
/ /)
// : new Container(
/ / width: 60.0.
/ / height: 60.0.
// decoration: new BoxDecoration(
// shape: BoxShape.circle,
// color: Colors.transparent,
// image: new DecorationImage(
// image: new NetworkImage(userAvatar),
// fit: BoxFit.cover),
// border: new Border.all(
// color: color. white, width: 2.0),
/ /),
// new Container(
// margin: const EdgeInsets. FromLTRB (0.0, 10.0, 0.0, 0.0),
// child: new Text(
// userName == null ? 'Click your avatar to login' : userName,
// style: new TextStyle(color: Colors. White, fontSize: 16.0),
/ /),
/ /)
/ /,
/ /)),
/ /),
// new SliverFixedExtentList(
// delegate:
// new SliverChildBuilderDelegate((BuildContext context, int index) {
// String title = titles[index];
// return new Container(
// alignment: Alignment.centerLeft,
// child: new InkWell(
// onTap: () {
// print("the is the item of $title");
/ /},
// child: new Column(
// children: 
      
       [
      
// new Padding(
// padding:
// const EdgeInsets. FromLTRB (15.0, 15.0, 15.0, 15.0),
// child: new Row(
// children: 
      
       [
      
// new Expanded(
// child: new Text(
// title,
// style: titleTextStyle,
/ /)),
// rightArrowIcon
/ /,
/ /),
/ /),
// new Divider(
/ / height: 1.0.
/ /)
/ /,
/ /),
/ /));
// }, childCount: titles.length),
/ / itemExtent: 50.0),
/ /]);
  }

  renderRow(context, i) {
    final userHeaderHeight = 200.0;
    if (i == 0) {
      var userHeader = new Container(
          height: userHeaderHeight,
          color: Colors.green,
          child: new Center(
              child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              userAvatar == null
                  ? new Image.asset(
                      "images/ic_avatar_default.png",
                      width: 60.0,) :new Container(
                      width: 60.0,
                      height: 60.0,
                      decoration: new BoxDecoration(
                          shape: BoxShape.circle,
                          color: Colors.transparent,
                          image: new DecorationImage(
                              image: new NetworkImage(userAvatar),
                              fit: BoxFit.cover),
                          border:
                              new Border.all(color: Colors.white, width: 2.0))),new Container(
                margin: const EdgeInsets.fromLTRB(0.0.10.0.0.0.0.0),
                child: new Text(
                  userName == null ? 'Click your avatar to log in' : userName,
                  style: new TextStyle(color: Colors.white, fontSize: 16.0),),),)));return new GestureDetector(
        onTap: () {
          Navigator.push(context,
              new MaterialPageRoute(builder: (context) => new LoginPage()));
        },
        child: userHeader,
      );
    }
    --i;
    if (i.isOdd) {
      return new Divider(
        height: 1.0,); } i = i ~/2;
    String title = titles[i];
    var listItemContent = new Padding(
      padding: const EdgeInsets.fromLTRB(10.0.15.0.10.0.15.0),
      child: new Row(
        children: <Widget>[
          new Expanded(
              child: new Text(
            title,
            style: titleTextStyle,
          )),
          rightArrowIcon
        ],
      ),
    );
    return new InkWell(
      child: listItemContent,
      onTap: () {},
    );
  }
}

Widget showCustomScrollView() {
  return new CustomScrollView(
    slivers: <Widget>[
      const SliverAppBar(
        pinned: true,
        expandedHeight: 250.0,
        flexibleSpace: const FlexibleSpaceBar(
          title: const Text('Demo'),),),new SliverGrid(
        gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
          // Maximum length of the horizontal axis
          maxCrossAxisExtent: 200.0.// Spindle spacing
          mainAxisSpacing: 10.0,
          crossAxisSpacing: 10.0.// Horizontal spacing
          childAspectRatio: 1.0,
        ),
        delegate: new SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return new Container(
              alignment: Alignment.center,
              color: Colors.teal[100 * (index % 9)],
              child: new Text('grid item $index')); }, childCount:20,),),new SliverFixedExtentList(
        itemExtent: 50.0,
        delegate:
            new SliverChildBuilderDelegate((BuildContext context, int index) {
          return new Container(
            alignment: Alignment.center,
            color: Colors.lightBlue[100 * (index % 9)],
            child: new Text('list item $index')); }, childCount:10),),,); } '### question: Does this actually reuse holder? Item more when can card.... Actually, I was so bored1000Multiple items (release version), the basic is not stuck, may not have studied the source code, can not see how reuse holder (also can not see whether reuse), here leave a question, slowly answer.Copy the code