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
) childrenState
inheritanceAutomaticKeepAliveClientMixin
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:
PageView
Children need to inherit fromStatefulWidget
PageView
The children ofState
Need 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:
- Create a new DART file in your own project, for example:
my_page_view.dart
- Copy the source code for PageView into this file, note: just copy it
PageView
and_PageViewState
You don’t need to copy the entire contents of the file - 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
- Modify the
cacheExtent
The value of the - In the use of
my_page_view.dart
When, there may be guide packet conflict, availablehide
Keyword to hide the system, orPageView
Let 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
- to
PageView
Add acacheCount
Represents the number of pages cached.Remember to add this attribute to all constructs _PageViewState
thebuild
The Widget returned by the method has a wrapper around itLayoutBuilder
Used to get the width and height of the control, and then modifycacheExtent
为widget.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
- with
PageView
That will beTabBarView
和_TabBarViewState
And the private constants used by the two classes (_kTabBarViewPhysics
) Copy it. - Guide packet resolve error
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_meizi/component/my_page_view.dart';
Copy the code
- Use keywords on guide packages
hide
Hide the SystemPageView
controls
import 'package:flutter/material.dart' hide PageView;
Copy the code