Reprint please indicate the source: juejin.cn/post/684490… This post is from: Wos homepage

Last updated: 2019-06-11


Preface:

Usually if you’re using PageView plus BottomNavigationBar or TabBarView plus TabBar you’ll find that when you go to another page, the previous page will be destroyed, and when you go back to the previous page, the page will be rebuilt, The data would then be reloaded, the controls would be re-rendered and the user experience would be terrible.

Here are some solutions:

Solution 1:

Using AutomaticKeepAliveClientMixin (official recommendation)

Since TabBarView also uses PageView internally, the solution is the same. Take PageView as an example

This method does not work well in the old version and needs to be updated to a newer version.

Flutter 0.5.8- pre-.277 • Channel Master • github.com/flutter/flu… Framework • Revision E5432A2843 (6 days ago) • 2018-08-08 16:45:08-0700 Engine • Tools • Dart 2.0.0 – dev. 69.5. Flutter – eab492385c

The above version was when I wrote this article, but I don’t know which version is the dividing line.

Run the following command to view the Flutter version

flutter –version

Switch the Flutter Channel(corresponding to its Git branch) by using the following command

flutter channel master

Master is the name of a channel. There are currently beta Dev and Master. Master > dev > beta in code update frequency

Specific practices:

letPageView(orTabBarView) childrenStateinheritanceAutomaticKeepAliveClientMixin

The following is an example:

import 'package:flutter/material.dart';

main() {
  runApp(MaterialApp(
    home: Test6(),
  ));
}

class Test6 extends StatefulWidget {
  @override
  Test6State createState() {
    return new Test6State();
  }
}

class Test6State extends State<Test6> {
  PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  Widget build(BuildContext context) {
    List<int> pages = [1, 2, 3, 4];
    List<int> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
    return Scaffold(
      appBar: AppBar(),
      body: PageView(
        children: pages.map((i) {
          return Container(
            height: double.infinity,
            color: Colors.red,
            child: Test6Page(i, data),
          );
        }).toList(),
        controller: _pageController,
      ),
    );
  }
}

class Test6Page extends StatefulWidget {
  final int pageIndex;
  final List<int> data;

  Test6Page(this.pageIndex, this.data);

  @override
  _Test6PageState createState() => _Test6PageState();
}

class _Test6PageState extends State<Test6Page> with AutomaticKeepAliveClientMixin {
  @override
  void initState() {
    super.initState();
    print('initState');
  }

  @override
  void dispose() {
    print('dispose');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: widget.data.map((n) {
        return ListTile(
          title: Text("The first${widget.pageIndex}The first page$nAn item")); }).toList(), ); } @override bool get wantKeepAlive =>true;
}
Copy the code

Conclusion:

  1. PageViewChildren need to inherit fromStatefulWidget
  2. PageViewThe children ofStateNeed to inherit fromAutomaticKeepAliveClientMixin

Note: some of my friends have reported that in some cases, such as jumping to a sub-page, the page will still refresh when you return.

Then you need to check with you the AutomaticKeepAliveClientMixin State

In AutomaticKeepAliveClientMixin source code, several methods are invoked the _ensureKeepAlive (), it is the key to the are able to keep fit.

This includes build(BuildContext Context). Before that you may have achieved the State of the build method, therefore did not notice the AutomaticKeepAliveClientMixin build methods have a @ mustCallSuper annotations.

The solution is to add super.build(context) to the top of the build method you implement;

It is also important to note that all other methods involved are implemented as required, using Lint tools.

Special thanks toSit quietly and often think about youStudents provide solutions to the above problems.


If the first method does not work for you, or if you are not planning to upgrade the Flutter version for the time being, try one of the following methods.

Solution 2:

Copy the PageView code and set the Viewport property cacheExtent to a large number

CacheExtent: 0.0. If you drop this assignment, you will end up caching a Widget using the default 250.0

This step is also required for TabBarView, as explained later

Concrete implementation:

  1. Create a new DART file in your own project, for example:my_page_view.dart
  2. Copy the source code for PageView into this file, note: just copy itPageViewand_PageViewStateYou don’t need to copy the entire contents of the file
  3. In case of error, it should be the guide package problem, according to the prompts to guide the package
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Copy the code
  1. Modify thecacheExtentThe value of the
  2. In the use ofmy_page_view.dartWhen, there may be guide packet conflict, availablehideKeyword to hide the system, orPageViewLet me just rename it
import 'package:flutter/material.dart' hide PageView;
Copy the code

When tested, a cacheExtent destroys P when offset by Pw + cacheExtent (P is the current page and Pw is the width of the current page).

For example: If PageView has three pages that open on the first page by default, cacheExtent: 0.0 then when you swipe right to reach the width of the first page, the first page is destroyed. That’s why PageView can’t preserve page state, right

Similarly, if cacheExtent: 1.0, the first page will not be destroyed by the time you reach the second page, but the first page will be destroyed by sliding one more (theoretical pixel) to the right.

For example, if the cacheExtent is a page width of -1, the first page won’t be destroyed until the third page is fully slid.

To summarize, if you want to cache all pages, use cacheExtent: double-.infinity

But if you want to be a little more flexible, you can “tweak” it a little bit

class PageView extends StatefulWidget {

  final intcacheCount; . }class _PageViewState extends State<PageView> {...@override
  Widget build(BuildContext context) {
    ...
    
    return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
      ...
            return new Viewport(
              cacheExtent: widget.cacheCount * constraints.maxWidth - 1,
              axisDirection: axisDirection,
              offset: position,
              slivers: <Widget>[
                newSliverFillViewport( viewportFraction: widget.controller.viewportFraction, delegate: widget.childrenDelegate), ], ); . }}Copy the code
  1. toPageViewAdd acacheCountRepresents the number of pages cached.Remember to add this attribute to all constructs
  2. _PageViewStatethebuildThe Widget returned by the method has a wrapper around itLayoutBuilderUsed to get the width and height of the control, and then modifycacheExtentwidget.cacheCount * constraints.maxWidth - 1

You can then assign a value to cacheCount when you use it

If it is TabBarView

Because the TabBarView encapsulates a PageView inside the TabBarView, modify the PageView as described above, and then replace the PageView inside the TabBarView with the modified PageView

  1. withPageViewThat will beTabBarView_TabBarViewStateAnd the private constants used by the two classes (_kTabBarViewPhysics) Copy it.
  2. Guide packet resolve error
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_meizi/component/my_page_view.dart';
Copy the code
  1. Use keywords on guide packageshideHide the SystemPageViewcontrols
import 'package:flutter/material.dart' hide PageView;
Copy the code