diff --git a/lib/main.dart b/lib/main.dart index aa713f8f..9e69cb73 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,10 +11,6 @@ import 'package:frosty/api/twitch_api.dart'; import 'package:frosty/constants/constants.dart'; import 'package:frosty/core/auth/auth_store.dart'; import 'package:frosty/screens/home/home.dart'; -import 'package:frosty/screens/home/stores/categories_store.dart'; -import 'package:frosty/screens/home/stores/home_store.dart'; -import 'package:frosty/screens/home/stores/list_store.dart'; -import 'package:frosty/screens/home/stores/search_store.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; import 'package:http/http.dart'; import 'package:mobx/mobx.dart'; @@ -154,9 +150,7 @@ class MyApp extends StatelessWidget { return Observer( builder: (context) { - final authStore = context.read(); final settingsStore = context.read(); - final twitchApi = context.read(); return MaterialApp( title: 'Frosty', @@ -167,29 +161,7 @@ class MyApp extends StatelessWidget { : settingsStore.themeType == ThemeType.light ? ThemeMode.light : ThemeMode.dark, - home: Home( - homeStore: HomeStore(), - followedStreamsStore: authStore.isLoggedIn - ? ListStore( - authStore: authStore, - twitchApi: twitchApi, - listType: ListType.followed, - ) - : null, - topSectionStore: ListStore( - authStore: authStore, - twitchApi: twitchApi, - listType: ListType.top, - ), - categoriesSectionStore: CategoriesStore( - authStore: authStore, - twitchApi: twitchApi, - ), - searchStore: SearchStore( - authStore: authStore, - twitchApi: twitchApi, - ), - ), + home: const Home(), ); }, ); diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index 711c1d42..24e71e8d 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/core/auth/auth_store.dart'; import 'package:frosty/screens/home/search/search.dart'; -import 'package:frosty/screens/home/stores/categories_store.dart'; import 'package:frosty/screens/home/stores/home_store.dart'; import 'package:frosty/screens/home/stores/list_store.dart'; -import 'package:frosty/screens/home/stores/search_store.dart'; import 'package:frosty/screens/home/top/top_section.dart'; import 'package:frosty/screens/home/widgets/streams_list.dart'; import 'package:frosty/screens/settings/settings.dart'; @@ -13,23 +11,11 @@ import 'package:frosty/screens/settings/stores/settings_store.dart'; import 'package:provider/provider.dart'; class Home extends StatelessWidget { - final HomeStore homeStore; - final ListStore? followedStreamsStore; - final ListStore topSectionStore; - final CategoriesStore categoriesSectionStore; - final SearchStore searchStore; - - const Home({ - Key? key, - required this.homeStore, - required this.followedStreamsStore, - required this.topSectionStore, - required this.categoriesSectionStore, - required this.searchStore, - }) : super(key: key); + const Home({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final homeStore = HomeStore(); final authStore = context.read(); return GestureDetector( @@ -66,12 +52,9 @@ class Home extends StatelessWidget { builder: (_) => IndexedStack( index: homeStore.selectedIndex, children: [ - if (authStore.isLoggedIn) StreamsList(store: followedStreamsStore!), - TopSection( - topSectionStore: topSectionStore, - categoriesSectionStore: categoriesSectionStore, - ), - Search(searchStore: searchStore), + if (authStore.isLoggedIn) const StreamsList(listType: ListType.followed), + const TopSection(), + const Search(), ], ), ), diff --git a/lib/screens/home/search/search.dart b/lib/screens/home/search/search.dart index c7f1a7fa..455ae646 100644 --- a/lib/screens/home/search/search.dart +++ b/lib/screens/home/search/search.dart @@ -1,28 +1,33 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:frosty/api/twitch_api.dart'; +import 'package:frosty/core/auth/auth_store.dart'; import 'package:frosty/screens/home/search/search_results_categories.dart'; import 'package:frosty/screens/home/search/search_results_channels.dart'; import 'package:frosty/screens/home/stores/search_store.dart'; import 'package:frosty/widgets/section_header.dart'; +import 'package:provider/provider.dart'; class Search extends StatefulWidget { - final SearchStore searchStore; - - const Search({Key? key, required this.searchStore}) : super(key: key); + const Search({Key? key}) : super(key: key); @override _SearchState createState() => _SearchState(); } class _SearchState extends State { + late final _searchStore = SearchStore( + authStore: context.read(), + twitchApi: context.read(), + ); + final _textEditingController = TextEditingController(); @override Widget build(BuildContext context) { const headerPadding = EdgeInsets.fromLTRB(15.0, 20.0, 15.0, 10.0); - final searchStore = widget.searchStore; return Column( children: [ Padding( @@ -48,17 +53,17 @@ class _SearchState extends State { icon: const Icon(Icons.clear), ), ), - onSubmitted: searchStore.handleQuery, + onSubmitted: _searchStore.handleQuery, ), ), Expanded( child: Observer( builder: (context) { - if (_textEditingController.text.isEmpty || searchStore.channelFuture == null || searchStore.categoryFuture == null) { + if (_textEditingController.text.isEmpty || _searchStore.channelFuture == null || _searchStore.categoryFuture == null) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (searchStore.searchHistory.isNotEmpty) + if (_searchStore.searchHistory.isNotEmpty) const Padding( padding: EdgeInsets.all(15.0), child: Text( @@ -68,7 +73,7 @@ class _SearchState extends State { ), Expanded( child: ListView( - children: searchStore.searchHistory + children: _searchStore.searchHistory .mapIndexed( (index, searchTerm) => ListTile( leading: const Icon(Icons.history), @@ -77,13 +82,13 @@ class _SearchState extends State { tooltip: 'Remove', icon: const Icon(Icons.cancel), onPressed: () => setState(() { - searchStore.searchHistory.removeAt(index); + _searchStore.searchHistory.removeAt(index); }), ), onTap: () { setState(() { _textEditingController.text = searchTerm; - searchStore.handleQuery(searchTerm); + _searchStore.handleQuery(searchTerm); }); }, ), @@ -103,7 +108,7 @@ class _SearchState extends State { ), ), SearchResultsChannels( - searchStore: searchStore, + searchStore: _searchStore, query: _textEditingController.text, ), const SliverToBoxAdapter( @@ -112,7 +117,7 @@ class _SearchState extends State { padding: headerPadding, ), ), - SearchResultsCategories(searchStore: searchStore), + SearchResultsCategories(searchStore: _searchStore), ], ); }, diff --git a/lib/screens/home/top/categories.dart b/lib/screens/home/top/categories.dart index 6e25ee2e..de1a55be 100644 --- a/lib/screens/home/top/categories.dart +++ b/lib/screens/home/top/categories.dart @@ -1,28 +1,30 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:frosty/api/twitch_api.dart'; +import 'package:frosty/core/auth/auth_store.dart'; import 'package:frosty/screens/home/stores/categories_store.dart'; import 'package:frosty/screens/home/widgets/category_card.dart'; import 'package:frosty/widgets/loading_indicator.dart'; import 'package:frosty/widgets/scroll_to_top_button.dart'; +import 'package:provider/provider.dart'; class Categories extends StatefulWidget { - final CategoriesStore store; - - const Categories({ - Key? key, - required this.store, - }) : super(key: key); + const Categories({Key? key}) : super(key: key); @override _CategoriesState createState() => _CategoriesState(); } class _CategoriesState extends State with AutomaticKeepAliveClientMixin { + late final _categoriesStore = CategoriesStore( + authStore: context.read(), + twitchApi: context.read(), + ); + @override Widget build(BuildContext context) { super.build(context); - final store = widget.store; final size = MediaQuery.of(context).size; final pixelRatio = MediaQuery.of(context).devicePixelRatio; @@ -33,11 +35,11 @@ class _CategoriesState extends State with AutomaticKeepAliveClientMi return RefreshIndicator( onRefresh: () async { HapticFeedback.lightImpact(); - await store.refreshCategories(); + await _categoriesStore.refreshCategories(); - if (store.error != null) { + if (_categoriesStore.error != null) { final snackBar = SnackBar( - content: Text(store.error!), + content: Text(_categoriesStore.error!), behavior: SnackBarBehavior.floating, ); ScaffoldMessenger.of(context).showSnackBar(snackBar); @@ -45,7 +47,7 @@ class _CategoriesState extends State with AutomaticKeepAliveClientMi }, child: Observer( builder: (_) { - if (store.categories.isEmpty && store.isLoading && store.error == null) { + if (_categoriesStore.categories.isEmpty && _categoriesStore.isLoading && _categoriesStore.error == null) { return const LoadingIndicator(subtitle: Text('Loading categories...')); } return Stack( @@ -53,17 +55,17 @@ class _CategoriesState extends State with AutomaticKeepAliveClientMi children: [ GridView.builder( physics: const AlwaysScrollableScrollPhysics(), - controller: store.scrollController, + controller: _categoriesStore.scrollController, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, ), - itemCount: store.categories.length, + itemCount: _categoriesStore.categories.length, itemBuilder: (context, index) { - if (index > store.categories.length / 2 && store.hasMore) { - store.getCategories(); + if (index > _categoriesStore.categories.length / 2 && _categoriesStore.hasMore) { + _categoriesStore.getCategories(); } return CategoryCard( - category: store.categories[index], + category: _categoriesStore.categories[index], width: artWidth, height: artHeight, ); @@ -72,7 +74,7 @@ class _CategoriesState extends State with AutomaticKeepAliveClientMi Observer( builder: (context) => AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: store.showJumpButton ? ScrollToTopButton(scrollController: store.scrollController) : null, + child: _categoriesStore.showJumpButton ? ScrollToTopButton(scrollController: _categoriesStore.scrollController) : null, ), ), ], @@ -87,7 +89,7 @@ class _CategoriesState extends State with AutomaticKeepAliveClientMi @override void dispose() { - widget.store.dispose(); + _categoriesStore.dispose(); super.dispose(); } } diff --git a/lib/screens/home/top/top_section.dart b/lib/screens/home/top/top_section.dart index 7c854552..d98dad4f 100644 --- a/lib/screens/home/top/top_section.dart +++ b/lib/screens/home/top/top_section.dart @@ -1,26 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:frosty/screens/home/stores/categories_store.dart'; import 'package:frosty/screens/home/stores/list_store.dart'; import 'package:frosty/screens/home/top/categories.dart'; import 'package:frosty/screens/home/widgets/streams_list.dart'; class TopSection extends StatelessWidget { - final ListStore topSectionStore; - final CategoriesStore categoriesSectionStore; - - const TopSection({ - Key? key, - required this.topSectionStore, - required this.categoriesSectionStore, - }) : super(key: key); + const TopSection({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return DefaultTabController( length: 2, child: Column( - children: [ - const TabBar( + children: const [ + TabBar( indicatorColor: Colors.deepPurpleAccent, tabs: [ Tab(text: 'Streams'), @@ -30,8 +22,8 @@ class TopSection extends StatelessWidget { Expanded( child: TabBarView( children: [ - StreamsList(store: topSectionStore), - Categories(store: categoriesSectionStore), + StreamsList(listType: ListType.top), + Categories(), ], ), ), diff --git a/lib/screens/home/widgets/streams_list.dart b/lib/screens/home/widgets/streams_list.dart index 2734f3fc..40bb5cea 100644 --- a/lib/screens/home/widgets/streams_list.dart +++ b/lib/screens/home/widgets/streams_list.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:frosty/api/twitch_api.dart'; +import 'package:frosty/core/auth/auth_store.dart'; +import 'package:frosty/models/category.dart'; import 'package:frosty/screens/home/stores/list_store.dart'; import 'package:frosty/screens/home/widgets/stream_card.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; @@ -9,20 +12,30 @@ import 'package:frosty/widgets/scroll_to_top_button.dart'; import 'package:provider/provider.dart'; class StreamsList extends StatefulWidget { - final ListStore store; + final ListType listType; + final CategoryTwitch? categoryTwitch; - const StreamsList({Key? key, required this.store}) : super(key: key); + const StreamsList({ + Key? key, + required this.listType, + this.categoryTwitch, + }) : super(key: key); @override _StreamsListState createState() => _StreamsListState(); } class _StreamsListState extends State with AutomaticKeepAliveClientMixin { + late final _listStore = ListStore( + authStore: context.read(), + twitchApi: context.read(), + listType: widget.listType, + categoryInfo: widget.categoryTwitch, + ); + @override Widget build(BuildContext context) { super.build(context); - final store = widget.store; - final size = MediaQuery.of(context).size; final pixelRatio = MediaQuery.of(context).devicePixelRatio; @@ -32,11 +45,11 @@ class _StreamsListState extends State with AutomaticKeepAliveClient return RefreshIndicator( onRefresh: () async { HapticFeedback.lightImpact(); - await store.refreshStreams(); + await _listStore.refreshStreams(); - if (store.error != null) { + if (_listStore.error != null) { final snackBar = SnackBar( - content: Text(store.error!), + content: Text(_listStore.error!), behavior: SnackBarBehavior.floating, ); ScaffoldMessenger.of(context).showSnackBar(snackBar); @@ -44,7 +57,7 @@ class _StreamsListState extends State with AutomaticKeepAliveClient }, child: Observer( builder: (_) { - if (store.streams.isEmpty && store.isLoading && store.error == null) { + if (_listStore.streams.isEmpty && _listStore.isLoading && _listStore.error == null) { return const LoadingIndicator(subtitle: Text('Loading streams...')); } return Stack( @@ -52,15 +65,15 @@ class _StreamsListState extends State with AutomaticKeepAliveClient children: [ ListView.builder( physics: const AlwaysScrollableScrollPhysics(), - controller: store.scrollController, - itemCount: store.streams.length, + controller: _listStore.scrollController, + itemCount: _listStore.streams.length, itemBuilder: (context, index) { - if (index > store.streams.length / 2 && store.hasMore) { - store.getStreams(); + if (index > _listStore.streams.length / 2 && _listStore.hasMore) { + _listStore.getStreams(); } return Observer( builder: (context) => StreamCard( - streamInfo: store.streams[index], + streamInfo: _listStore.streams[index], width: thumbnailWidth, height: thumbnailHeight, showUptime: context.read().showThumbnailUptime, @@ -71,7 +84,7 @@ class _StreamsListState extends State with AutomaticKeepAliveClient Observer( builder: (context) => AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: store.showJumpButton ? ScrollToTopButton(scrollController: store.scrollController) : null, + child: _listStore.showJumpButton ? ScrollToTopButton(scrollController: _listStore.scrollController) : null, ), ), ], @@ -86,7 +99,7 @@ class _StreamsListState extends State with AutomaticKeepAliveClient @override void dispose() { - widget.store.dispose(); + _listStore.dispose(); super.dispose(); } }