Использование одного и того же поставщика состояния riverpod для нескольких страниц

#flutter #provider #riverpod

#flutter #поставщик #riverpod

Вопрос:

Я использую поставщика для управления состоянием (на самом деле riverpod) В моем проекте у меня есть tabview, и я использую каждую вкладку для категории, и каждая показывает список новостей. размер tabview не является фиксированным, и я использую одну и ту же страницу для всех вкладок и передаю идентификатор категории на страницу.

Проблема в том, что newsProvider всегда сохраняет идентификатор последней категории, и на всех страницах отображается один и тот же список новостей. Кто я могу разделить список новостных провайдеров для каждой страницы?

 //create tabview in my stateFullWidget
@override
Widget build(BuildContext context) {
   return Scaffold(
   body: DefaultTabController(
     length: europeanCountries.length, // europeanCountries has dynamic size
         child: new Scaffold(
            appBar: new AppBar(
                 
            flexibleSpace: new Column( 
               children: [
                  new TabBar( 
                      tabs: europeanCountries.map<Widget>((e) => getTab(e, FontAwesomeIcons.newspaper)).toList()),
                    ],
                  ),
                ),

               // dynamically create NewsList and pass category id
                body: TabBarView(children: europeanCountries.map((catItem) => NewsList(category: Category(title: catItem.title, id:catItem.id))).toList()),
              ),
            )
       )
 }

class NewsList extends StatefulWidget {
  Category category;

  NewsList({this.category});

  @override
  State<StatefulWidget> createState() {
    return _NewsListState(category:category);
  }
}

class _NewsListState extends State<NewsList> with AutomaticKeepAliveClientMixin<NewsList> {
  Category category;

  _NewsListState({this.category});
  
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      context.read(newsProvider).getNewsList(category.id);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Consumer(builder: (ctx, watch, child) {
      return watch(newsProvider.state).map(
        init: (value) {
          return Container();
        },
        loading: (value) {
          return Container(
            child: Center(
              child: CircularProgressIndicator(),
            ),
          );
        },
        success: (value) {
          return Container(child: getNewsItem(value.newsList));
        },
        serverError: (value) {
          return Container();
        },
      );
    });
  }

  @override
  bool get wantKeepAlive => true;
}




final newsProvider = StateNotifierProvider.autoDispose<NewsListNotifier>((ref) {
  var newsService = ref.watch(newsServiceProvider);
  return NewsListNotifier(newsService);
});

class NewsListNotifier extends StateNotifier<NewsListState> {
  final NewsService serviceRepository;


  NewsListNotifier(this.serviceRepository) : super(NewsListState.init());


  getNewsList(int catID) async {
    state = NewsListState.loading();
    DataResponse request = await serviceRepository.getNewsList(catID);
    request.maybeWhen(
      success: (value) {

        if (value.data != null) {
          state = NewsListState.success(newsList: value.data);
        }
      },
      error: (error) {
        state = NewsListState.serverError(error);
      },
      orElse: () {},
    );
  }

}
 

Комментарии:

1. вы пробовали использовать family для получения разных запросов на основе их идентификатора? riverpod.dev/docs/ concepts/modifiers/family

2. Да, это работает с использованием семейства

Ответ №1:

Я, наконец, нахожу ответ. спасибо @EdwynZN Используя ключевое слово family, я могу создать что-то вроде семейства для каждой категории

 class NewsList extends StatefulWidget {
  Category category;

  NewsList({this.category});

  @override
  State<StatefulWidget> createState() {
    return _NewsListState(category:category);
  }
}


class _NewsListState extends State<NewsList> with AutomaticKeepAliveClientMixin<NewsList> {
  Category category;

  _NewsListState({this.category});
  
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
     
      // add category.id to newsProvider, instead of getNewsList()
      context.read(newsProvider(category.id)).getNewsList();
    });
  }


  @override
  Widget build(BuildContext context) {
    return Consumer(builder: (ctx, watch, child) {

      //newsProvider(category.id) just listen to specific category id and not listen to all ids
      return watch(newsProvider(category.id).state).map( 
        init: (value) {
          return Container();
        },
        loading: (value) {
          return Container(
            child: Center(
              child: CircularProgressIndicator(),
            ),
          );
        },
        success: (value) {
          return Container(child: getNewsItem(value.newsList));
        },
        serverError: (value) {
          return Container();
        },
      );
    });
  }


  @override
  bool get wantKeepAlive => true;
}

//using family here and pass <My State, Category id>
final newsProvider = StateNotifierProvider.family<NewsListNotifier, int>((ref, catID) {
  var newsService = ref.watch(newsServiceProvider);
  return NewsListNotifier(newsService, catID);
});

class NewsListNotifier extends StateNotifier<NewsListState> {
  final NewsService serviceRepository;
  int catID;

  NewsListNotifier(this.serviceRepository, this.catID) : super(NewsListState.init());


  getNewsList() async {
    state = NewsListState.loading();
    DataResponse request = await serviceRepository.getNewsList(catID, page, 20);

    request.maybeWhen(
      success: (value) {

        if (value.data != null) {
          state = NewsListState.success(newsList: value.data);
        }
      },
      error: (error) {
        state = NewsListState.serverError(error);
      },
      orElse: () {},
    );
  }

}