Hello everyone, after several months of diving, Flutter is unexpectedly hot. Sorry for not updating it. After joining the start-up team and experiencing several ups and downs, Flutter has finally made time to learn about technology. This article will briefly share the flutter technology used by The Portal Web APP.

In the previous practice project of news APP, the usage of Tab+TabBarView+Tabcontroller was used to realize the page switching based on the scaffold top Tab page. However, the problem of TabBarView redrawing was caused by switching between the top Tab page. The page cannot stay in the state before switching, this problem is also troubled me for a long time, use PageStorageKey with Stack+Offstage to solve this problem.

First, let’s write our own TabBar for fun. Why? Because this can achieve a high degree of control customization, incidentally learn a new component usage:

class NewsTab { String text; String tab; NewsTab(this.text,this.tab); Final List<NewsTab> NewsTabs = <NewsTab>[new NewsTab(' financial','financial'), New NewsTab(' technology','technology'), new NewsTab(' medical','medical'),]; class TabNavigation extends StatelessWidget { TabNavigation({this.currentTab, this.onSelectTab}); final NewsTab currentTab; final ValueChanged<NewsTab> onSelectTab; @override Widget build(BuildContext context) {return Row(children: NewsTabs. Map ((item){return GestureDetector(// GestureDetector) EdgeInsets. FromLTRB (24.0, 0.0, 24.0, 0.0), child: Text(item. Text,style: TextStyle(color: _colorTabMatching(item: Item),),),), onTap: ()=>onSelectTab(item,); }).toList() ); } return currentTab == item {return currentTab == item? Colors.black : Colors.grey; }}Copy the code

Why do you do that? Since we can control the external data using the onSelectTab function, TabNavigation is called on the main page:

class _MainListState extends State<MainList> { NewsTab _currenttab = NewsTabs[0]; Void _selectTab(NewsTab Tab){setState(() {_currentTab = Tab; }); } TabNavigation( currentTab: _currenttab, onSelectTab: _selectTab, ), .... }Copy the code

When using TabNavigation, pass the defined _selectTab function to it to complete the task of state value modification. This is also a way for the child control to pass parameters to the parent control, especially suitable for the scenario when the child control changes the state value of the parent control.

This is the definition of the Tab Tab and the home page.

class NewsList extends StatefulWidget{ @override NewsList({this.newsType,this.pageKey}); final PageStorageKey<NewsTab> pageKey; // The current control uniquely identifies the Key Final String newsType; NewsListState createState() => new NewsListState(); } class NewsListState extends State<NewsList>{ .... }Copy the code


in PageStorageKey

.

in PageStorageKey PageStorageKey is a local Key that should not be repeated in the parent control, so I use the NewsTab type. Of course, friends can define other values that will not be repeated, but this may be a bit more difficult than I do.


class MainList extends StatefulWidget { const MainList({ Key key }) : super(key: key); @override _MainListState createState() => new _MainListState(); } class _MainListState extends State<MainList> {// Define a Key. Map<NewsTab, PageStorageKey<NewsTab>> pageKeys = {NewsTabs[0]: PageStorageKey<NewsTab>(NewsTabs[0]), NewsTabs[1]: PageStorageKey<NewsTab>(NewsTabs[1]), NewsTabs[2]: PageStorageKey<NewsTab>(NewsTabs[2]), }; . Widget build(BuildContext context){ return Scaffold( ... Body: Stack(//Stack renders all the children when initialized, while TabBarView renders only the default children: Newstabs.map ((item) {return Offstage(// use Offstage to hide child controls that do not need to be displayed Offstage: _currentTab! = item, child: NewsList(pageKey: pageKeys[item], // newsType: item.tab); }).toList(), ) } } }Copy the code

The combination of Stack and Offstage can be seen in the comments. Because these two controls retain the properties of the child controls, together with the PageStorageKey

identifier, NewsList’s position in the control tree remains the same. This avoids the problem of repeated rendering after NewsList is toggled.

To make things easier to understand, I separate the definition of pageKeys from the use of pageKeys. When the NewsList is initialized, a key of type PageStorageKey

is assigned to it so that it is not considered a new control by flutter if it needs to be used repeatedly. It won’t trigger a redraw. Of course you could write:

NewsList(
   pageKey: PageStorageKey<NewsTab>(item),
   newsType: item.tab),
  )
Copy the code

Either way, you can always iterate through NewsTabs to get NewsTab, which makes it easier to create PageStorageKey.

Why not GlobalKey? This is because globalKeys are expensive and exist throughout the lifecycle of the APP, like global variables. On the other hand, global variables are not allowed to be defined twice. In case you need to recreate the same control elsewhere, you have to think of ways to avoid the same GlobalKey, so as not to cause problems. As a side note, only stateful controls can use GlobalKey, as you can see from the GlobalKey definition: GlobalKey

> is used by the State class under StatefulWidget.

Tab+TabBarView+Tabcontroller + PageStorageKey+Stack+Offstage

It can be seen that the difference between the two in initial rendering and switching of pages is that the former only renders a Tab page during initialization, each Tab page will be automatically disposed during page switching and the new page should be re-initstate, while the latter renders all sub-tab pages during initialization and there is no dispose during page switching. Instead, only the didChangeDependencies event of the stateful control is called.

Source address please click here, this share only solutions to think about, maybe there are better solutions, welcome to share.

Here to thankJarvanMotheshareWe have the above solution

In addition, let me summarize a few small questions

1. After the project is packaged with APK, modify the code again and run. There is a certain probability that the new code will not take effect. Open the folder under flutter: [your address]\flutter\bin 2). 3). Command line: flutter doctor 4). After the process is complete, flutter Run again

I thought that the problem could be solved by simply using the command “flutter clean” to delete the build folder under the project directory and run it again.

flutter clean

Flutter create -i objc.

  • -I is the iOS project development language, objc and Swift. Objc is the default.
  • -A indicates the Android project development language, Java and Kotlin, where Java is the defaultHere to thankJarvanMotheshare

2. UseNavigatorWhen making a page jump, remember to add it to the parent control constructor or function that uses itBuildContextattribute

The BuildContext property in flutter is the anchor point of the control in the control tree. It can also be understood as an index. When you need to jump to a page, you need to tell the Navigator the anchor point of the current control, so that when you click the back button in a new page, you can go back to the original page. The Navigator actually builds a stack of anchor points based on this anchor point, so when you need to trigger a page jump for a heavily written child control, you need to pass the context argument from the top parent control down one level. The purpose of adding BuildContext to the control function is to let the control know who I am, where I came from, and where I am going, for example:

_list(BuildContext context, List dataList){.... Return ListView.builder(// padding: const EdgeInsets. All (16.0), itemCount: dataList. Length, itemBuilder: (context, I) {// The context argument corresponds to the current control's anchor point in the control tree, Return _newsRow(dataList[I],context); return _newsRow(dataList[I],context); }); } _newsRow(Map newsInfo,BuildContext context){return ListTile(... OnTap: (){navigator.of (context).push(// until used by navigator.of (context). (BuildContext context) => NewsDetail( id:newsInfo["id"].toString() ) ) ); }); }Copy the code

So what is a control tree? The whole Flutter project is a tree of controls with N parent and child controls connected together.

3. The json data obtained from the network contains arrays, which cannot be directly added by list.add () or list.addall ().

  • Used to store dataListObject that must be initialized or called directlylist.add()Will be submitted to thenullError:

List list = new List();

  • If the obtained JSON data key-value pair has data, it cannot be directly assigned to the defined List

    List, and the data needs to be reassembled, because the obtained JSON key-value pair contains data in this format:

    / / get the json data data: {items: [{' k1 ':' v1 '}, {' k2: "v2"}, {' k3 ':' the v3}, {' k4 ':' the v4 '},... }Copy the code

I directly assigned the list variable defined above:

    List<Map> list = new List();
    list = request['data']['items'];
Copy the code

The result is tragic, keep reporting this error:

Json data from the network request is Iterable

> by default, and cannot be assigned to the List directly, so we need to do something like this:

List<Map> a = new List(); A.dd () if (request['success']==true){for(int I =0; i<request['data']['items'].length; i++){ a.add(request['data']['items'][i]); // } return a; DataList}else return null; dataList}else return null;Copy the code

This is much better, of course, if you do json serialization, ignore this problem.

If you want to learn more about Flutter techniques, please follow the Flutter circle. You are welcome to contribute to the Flutter community here. You can also join the Flutter Chinese community (official QQ group: 338252156) and grow together