# 17.请求数据保留

### 前言

前文中可以发现一个问题，每次切换页面，都会重新请求数据。而实际开发中，我们会对数据进行保留，在需要的时候再进行数据更新。

![1](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-5335b8d3f41b9ea76f5b1adc474558eac7591aa8%2F16-05.gif?alt=media)

### Mixin

#### 调整继承关系

多继承 `AutomaticKeepAliveClientMixin`

```
class MessagePage extends StatefulWidget {
  @override
  _MessagePageState createState() => _MessagePageState();
}

class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClientMixin<MessagePage> {
    ...
}
```

#### 重写 wantKeepAlive

```
class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClientMixin<MessagePage> {
    ...

    @override
    bool get wantKeepAlive => true;
}
```

#### 调整 build 方法

```
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }
```

#### 调整 MyHomePage （标签Tab页）

原先我们的切换Tab的逻辑是这样的：

```
class _MyHomePageDataSource extends State<MyHomePage> {
  int _selectedTab = 0;
  final List<Widget> _pages = [
      MessagePage(), 
      ContactsPage(), 
      MommentPage(), 
      MinePage()
      ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _pages[_selectedTab],
      ...
    );
```

并非所有的子页面的 `widget` 都在标签容器的 `widget` 树中，导致每次切换都会去重新生成渲染。

* 我们需要做如下调整：
  * 构建 `PageController`
  * 调整 `Body` 为 `PageView`， 并绑定 `PageController` 和 `pages`
  * 通过 `PageController` 切换子页面

```
class _MyHomePageDataSource extends State<MyHomePage> {
  int _selectedTab = 0;
  final List<Widget> _pages = [
    MessagePage(),
    ContactsPage(),
    MommentPage(),
    MinePage()
  ];
  // 一、 构建 PageController
  final PageController _pageController = PageController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // 二、 调整 Body 为 PageView， 并绑定 PageController 和 pages
      body: PageView(
        controller: _pageController,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Colors.green,
        unselectedItemColor: Colors.grey,
        currentIndex: _selectedTab,
        onTap: (idx) {
          setState(() {
            _selectedTab = idx;
            // 三、 通过 PageController 切换子页面
            _pageController.jumpToPage(_selectedTab);
          });
        },
        items: const [...],
      ),
    );
  }
}
```

#### 检查效果

![1](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-c36463f0446a90e7146c4e3a1f73be4f5ae9d03e%2F17-01.gif?alt=media)

### PageView

#### 滑动切换子页面

![2](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-043a1e7084ed85e12ff2724721ab66df4fdc4d75%2F17-02.gif?alt=media)

在使用总突然发现可以通过滑动切换 PageView 的子页面。但是底部的标签选中没有变化，下面我们来处理一下。

#### 同步标签切换

通过 onPageChanged 来监听页面的切换，同步修改选中标签。

```
body: PageView(
        onPageChanged: (idx) {
          setState(() {
            _selectedTab = idx;
          });
        },
        controller: _pageController,
        children: _pages,
      ),
```

#### 关闭滑动切换

当然，不需要滑动的话可以通过 physics 来进行关闭。

```
body: PageView(
    physics: const NeverScrollableScrollPhysics(),
    controller: _pageController,
    children: _pages,
),
```
