Refactor home

This commit is contained in:
Tommy Chow 2022-01-25 16:38:43 -05:00
parent 26d35a31fe
commit cac7e06791
6 changed files with 76 additions and 109 deletions

View file

@ -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<AuthStore>();
final settingsStore = context.read<SettingsStore>();
final twitchApi = context.read<TwitchApi>();
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(),
);
},
);

View file

@ -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<AuthStore>();
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(),
],
),
),

View file

@ -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<Search> {
late final _searchStore = SearchStore(
authStore: context.read<AuthStore>(),
twitchApi: context.read<TwitchApi>(),
);
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<Search> {
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<Search> {
),
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<Search> {
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<Search> {
),
),
SearchResultsChannels(
searchStore: searchStore,
searchStore: _searchStore,
query: _textEditingController.text,
),
const SliverToBoxAdapter(
@ -112,7 +117,7 @@ class _SearchState extends State<Search> {
padding: headerPadding,
),
),
SearchResultsCategories(searchStore: searchStore),
SearchResultsCategories(searchStore: _searchStore),
],
);
},

View file

@ -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<Categories> with AutomaticKeepAliveClientMixin {
late final _categoriesStore = CategoriesStore(
authStore: context.read<AuthStore>(),
twitchApi: context.read<TwitchApi>(),
);
@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<Categories> 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<Categories> 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<Categories> 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<Categories> 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<Categories> with AutomaticKeepAliveClientMi
@override
void dispose() {
widget.store.dispose();
_categoriesStore.dispose();
super.dispose();
}
}

View file

@ -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(),
],
),
),

View file

@ -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<StreamsList> with AutomaticKeepAliveClientMixin {
late final _listStore = ListStore(
authStore: context.read<AuthStore>(),
twitchApi: context.read<TwitchApi>(),
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<StreamsList> 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<StreamsList> 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<StreamsList> 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<SettingsStore>().showThumbnailUptime,
@ -71,7 +84,7 @@ class _StreamsListState extends State<StreamsList> 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<StreamsList> with AutomaticKeepAliveClient
@override
void dispose() {
widget.store.dispose();
_listStore.dispose();
super.dispose();
}
}