带NestedScrollview的RefreshIndicator

时间:2018-09-10 19:26:48

标签: dart flutter

我想要2个带有ListView的标签页共享一个RefreshIndicator。但是,一个RefreshIndicator必须具有Scrollable作为子级(不是TabBarView),因此我改为尝试每个标签制作2个RefreshIndicators,如下面的代码所示。

但这带来了一个不同的问题,我还想要一个浮动的AppBar,这意味着我必须使用NestedScrollView。因此,无论何时向下滚动,我最终都会触发两个RefreshIndicators的onRefresh方法。而我只需要刷新一下即可。

import 'package:flutter/material.dart';

main() {
    runApp(
        MaterialApp(
            home: DefaultTabController(
                length: 2,
                child: Scaffold(
                    body: NestedScrollView(
                        headerSliverBuilder: (context, innerBoxIsScrolled) {
                            return [
                                SliverAppBar(
                                    floating: true,
                                    snap: true,
                                    bottom: TabBar(
                                        tabs: [
                                            Tab(text: 'Page1'),
                                            Tab(text: 'Page2'),
                                        ],
                                    ),
                                ),
                            ];
                        },
                        body: TabBarView(
                            children: [
                                Page(1),
                                Page(2),
                            ],
                        ),
                    ),
                ),
            ),
        ),
    );
}

class Page extends StatefulWidget {
    final pageNumber;
    Page(this.pageNumber);
    createState() => PageState();
}

class PageState extends State<Page> with AutomaticKeepAliveClientMixin {
    get wantKeepAlive => true;

    build(context){
        super.build(context);
        return RefreshIndicator(
            onRefresh: () => Future(() async {
                print('Refreshing page no. ${widget.pageNumber}');  // This prints twice once both tabs have been opened
                await Future.delayed(Duration(seconds: 5));
            }),
            child: ListView.builder(
                itemBuilder: ((context, index){
                    return ListTile(
                        title: Text('Item $index')
                    );
                }),
            )
        );
    }
}

AutomaticKeepAliveClientMixin可以防止每次切换标签时页面重建,因为在我的实际应用中这将是一个昂贵的过程。

对于两个选项卡都使用单个RefreshIndicator的解决方案将是最理想的,但是可以提供任何帮助。

3 个答案:

答案 0 :(得分:1)

如果要浮动应用程序栏,则必须使用嵌套滚动视图和条状应用程序栏。当您尝试在列表选项卡视图的子列表中使用刷新指示器时,刷新指示器不起作用。这仅仅是因为嵌套的滚动视图。

如果您假设有两个列表作为选项卡视图的子级,则您一次只想刷新一个或两个,则请遵循以下代码。

用刷新指示器将嵌套的滚动视图包裹起来,然后在刷新部分

RefreshIndicator(
    color: Colors.red,
    displacement: 70,


    onRefresh: _refreshGeneralList,
    key: _refreshIndicatorKey,
    child: NestedScrollView(


      headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[

          SliverAppBar(

            centerTitle: true,
            title: Text(
              "App Bar",
              style: TextStyle(
                  color: Colors.black,
                  fontSize: 14,
            ),
            leading: Padding(
              padding: const EdgeInsets.only(left: 8.0),
              child: IconButton(
                icon: Icon(Icons.profile),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ),
            actions: [
              InkWell(
                  onTap: () {
                    setState(() {
                      isPremium = !isPremium;
                    });
                  },
                  child: Icon(
                    Icons.monetization_on,
                    color: isPremium ? Colors.green : Colors.blueGrey,
                    size: 33,
                  )),
              SizedBox(
                width: 25,
              )
            ],

            elevation: 0,
            backgroundColor: Colors.white,
            pinned: true,
            floating: true,
            forceElevated: innerBoxIsScrolled,
            bottom: isPremium
                ? TabBar(

                labelStyle: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w600),
                labelColor: Colors.blueGrey,
                indicatorColor:Colors.red,
                unselectedLabelColor:
                Colors.green,
                labelPadding: EdgeInsets.symmetric(vertical: 13.5),
                controller: _tabController,
                tabs: [
                  Text(
                    "General",
                  ),
                  Text(
                    "Visitors",
                  ),
                ])
                : null,
          )
        ];
      },

      body: isPremium
          ? TabBarView(

          controller: _tabController,
          children: [
        generalNotificationsList(context),
        visitorsNotificationsList(context),
      ])
          : generalNotificationsList(context),
    ),
  ),

添加一个调用未来的函数。在以后的部分中,如果滚动标签栏视图的一个或两个子级,我们将编写代码。

 Future _refreshGeneralList() async{
print('refreshing ');
GeneralNotificationBloc().add(LoadGeneralNotificationEvent(context));
PremiumNotificationBloc().add(LoadPremiumNotificationEvent(context));
return Future.delayed(Duration(seconds: 1));

}

答案 1 :(得分:0)

可以用 RefreshIndicator 包裹整个 NestedScrollView 并更新 notificationPredicate:

       DefaultTabController(
          length: tabs.length,
          child: RefreshIndicator(
            notificationPredicate: (notification) {
              // with NestedScrollView local(depth == 0) OverscrollNotification are not sent
              if (notification is OverscrollNotification) {
                return notification.depth == 2;
              }
              return notification.depth == 0;
            },
            onRefresh: () => Future.value(null),
            child: NestedScrollView(
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                return [
                  SliverAppBar(...)
                ];
              },
              body: TabBarView(
                children: tabs,
              ),
            ),
          ),
        )

或者使用

答案 2 :(得分:0)

此代码对我有用,它主要建立在@Nuts 答案之上,但需要进行一些调整,我的编辑被拒绝了

DefaultTabController(
      length: tabs.length,
      child: RefreshIndicator(
        notificationPredicate: (notification) {
          // with NestedScrollView local(depth == 2) OverscrollNotification are not sent
          return notification.depth == 2;
        },
        onRefresh: () => Future.value(null),
        child: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return [
              SliverAppBar(...)
            ];
          },
          body: TabBarView(
            children: tabs,
          ),
        ),
      ),
    )