PageView внутри NestedScrollView, keepAlive с AutomaticKeepAliveClientMixin, все ListView внутри каждого PageView будут использовать одно и то же поведение прокрутки - PullRequest
1 голос
/ 06 мая 2020

У меня есть страница со структурой типа NestedScrollView> PageView> ListView, проблема в том, что когда я прокручиваю внутри списка, другие списки прокручиваются одинаково, как будто они используют один и тот же контроллер прокрутки.

enter image description here

Я сделал демонстрацию ниже и проверил, что если нет NestedScrollView или AutomaticKeepAliveClientMixin, все будет в порядке. Кто-нибудь знает, как исправить?

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  static const List<String> tabs = ['tender', 'prequalification', 'winCandidate', 'winBid'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            /// * Something else
            Container(
              width: MediaQuery.of(context).size.width,
              height: 100.0,
              color: Colors.orange,
              child: Center(
                child: Text(
                  'Something else...',
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 40.0,
                  ),
                ),
              ),
            ),
            /// * Nested ScrollView
            Expanded(
              child: NestedScrollView(
                controller: ScrollController(),
                headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                  return <Widget>[
                    SliverOverlapAbsorber(
                      handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                      child: SliverAppBar(
                        title: Text('NestedScrollView AppBar'),
                        expandedHeight: 90.0,
                        forceElevated: innerBoxIsScrolled,
                      ),
                    ),
                  ];
                },
                body: NotificationListener<ScrollNotification>(
                  onNotification: (ScrollNotification _) => true,
                  /// * PageView
                  child: PageView(
                    physics: BouncingScrollPhysics(),
                    controller: PageController(),
                    onPageChanged: (int page) {},
                    children: <Widget>[
                      ...List.generate(
                        4,
                        (int index) => BulletinPageView(
                          /// * to keep scroll position of each pageview
                          key: PageStorageKey<int>(index),
                          pageIndex: index,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

///***
/// * each pageview content, inside the NestedScrollView
/// */
class BulletinPageView extends StatefulWidget {

  const BulletinPageView({
    Key key,
    @required this.pageIndex,
  }) : super(key: key);

  final int pageIndex;

  @override
  _BulletinPageViewState createState() => _BulletinPageViewState();

}

class _BulletinPageViewState extends State<BulletinPageView>
  /// * each time page changed, no need to refetch
  with AutomaticKeepAliveClientMixin {

  @override
  bool get wantKeepAlive => true;

  Future<List<int>> _getDataList;

  @override
  void initState() {
    super.initState();

    _getDataList = _fetchDataList();
  }

  Future<List<int>> _fetchDataList() async {
    await Future.delayed(Duration(milliseconds: 1500));

    final int length = (Random().nextInt(5) + 1) * 20;

    final List<int> dataList = List<int>.generate(length, (int i) => i);

    return dataList;
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    return FutureBuilder(
      future: _getDataList,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.active:
          case ConnectionState.waiting:
            return Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            }

            return Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Center(
                  child: Icon(
                    Icons.autorenew,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 8.0),
                  child: Center(
                    child: Text(
                      'PageView ${widget.pageIndex}',
                      style: TextStyle(
                        color: Colors.red,
                      ),
                    ),
                  ),
                ),
                Flexible(
                  fit: FlexFit.loose,
                  child: DataListView(
                    onNotification: _onNotification,
                    dataList: snapshot.data,
                  ),
                ),
                Center(
                  child: Icon(
                    Icons.cached,
                  ),
                ),
              ],
            );
          default:
            return Container();
        }
      },
    );
  }

  bool _onNotification(ScrollNotification scrollNotification) {
    if (scrollNotification is ScrollStartNotification) {
      ScrollStartNotification(
        context: context,
        metrics: scrollNotification.metrics,
      ).dispatch(context);
    } else if (scrollNotification is ScrollUpdateNotification) {
      ScrollUpdateNotification(
        context: context,
        metrics: scrollNotification.metrics,
      ).dispatch(context);
    } else if (scrollNotification is ScrollEndNotification) {
      ScrollEndNotification(
        context: context,
        metrics: scrollNotification.metrics,
      ).dispatch(context);
    }

    // do something

    return true;
  }

}

///***
/// * most inside data listview
/// */
class DataListView extends StatelessWidget {

  const DataListView({
    Key key,
    @required this.onNotification,
    @required this.dataList,
  }) : super(key: key);

  final bool Function(ScrollNotification) onNotification;

  final List<int> dataList;

  @override
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: onNotification,
      child: ListView.builder(
        physics: BouncingScrollPhysics(),
        padding: EdgeInsets.zero,
        shrinkWrap: true,
        itemCount: dataList.length,
        itemBuilder: (BuildContext context, int index) => Card(
          child: ListTile(
            leading: Icon(Icons.album),
            title: Text('No.${dataList[index]}'),
            subtitle: Text('subtitle...'),
          ),
        ),
      ),
    );
  }

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...