Add categories screen, store, & card (#42)

This commit is contained in:
tommyxchow 2021-11-21 02:23:58 -05:00
parent 051b8678e5
commit d1bd25eebf
5 changed files with 141 additions and 0 deletions

View file

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/stores/auth_store.dart';
import 'package:frosty/stores/categories_store.dart';
import 'package:frosty/widgets/category_card.dart';
import 'package:provider/provider.dart';
class Categories extends StatelessWidget {
const Categories({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final categoriesStore = CategoriesStore(authStore: context.read<AuthStore>());
return Observer(
builder: (_) {
return ListView.builder(
itemCount: categoriesStore.categories.length,
itemBuilder: (context, index) {
if (index > categoriesStore.categories.length / 2 && categoriesStore.hasMore) {
categoriesStore.getGames();
}
return CategoryCard(category: categoriesStore.categories[index]);
},
);
},
);
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/categories.dart';
import 'package:frosty/screens/search.dart';
import 'package:frosty/screens/settings.dart';
import 'package:frosty/stores/auth_store.dart';
@ -38,6 +39,7 @@ class _HomeState extends State<Home> {
final titles = [
if (context.read<AuthStore>().isLoggedIn) 'Followed Streams',
'Top Streams',
'Categories',
'Search',
];
return Text(
@ -75,6 +77,7 @@ class _HomeState extends State<Home> {
category: StreamCategory.top,
streamListStore: widget.streamListStore,
),
const Categories(),
const Search(),
],
);
@ -94,6 +97,10 @@ class _HomeState extends State<Home> {
icon: Icon(Icons.arrow_upward),
label: 'Top',
),
const BottomNavigationBarItem(
icon: Icon(Icons.games),
label: 'Categories',
),
const BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',

View file

@ -0,0 +1,37 @@
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/stores/auth_store.dart';
import 'package:mobx/mobx.dart';
part 'categories_store.g.dart';
class CategoriesStore = _CategoriesStoreBase with _$CategoriesStore;
abstract class _CategoriesStoreBase with Store {
final categories = ObservableList<CategoryTwitch>();
String? currentCursor;
var _isLoading = false;
bool get hasMore => _isLoading == false && currentCursor != null;
final AuthStore authStore;
_CategoriesStoreBase({required this.authStore}) {
getGames();
}
@action
Future<void> getGames() async {
_isLoading = true;
final result = await Twitch.getTopGames(headers: authStore.headersTwitch, cursor: currentCursor);
if (result != null) {
categories.addAll(result.data);
currentCursor = result.cursor;
}
_isLoading = false;
}
}

View file

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'categories_store.dart';
// **************************************************************************
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$CategoriesStore on _CategoriesStoreBase, Store {
final _$getGamesAsyncAction = AsyncAction('_CategoriesStoreBase.getGames');
@override
Future<void> getGames() {
return _$getGamesAsyncAction.run(() => super.getGames());
}
@override
String toString() {
return '''
''';
}
}

View file

@ -0,0 +1,44 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/models/category.dart';
/// A tappable card widget that displays a category's box art and name.
class CategoryCard extends StatelessWidget {
final CategoryTwitch category;
const CategoryCard({Key? key, required this.category}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
child: Row(
children: [
Flexible(
flex: 1,
child: CachedNetworkImage(
imageUrl: category.boxArtUrl.replaceFirst('-{width}x{height}', '-138x184'),
),
),
const SizedBox(width: 10),
Flexible(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const Text('0 Viewers')
],
),
),
],
),
),
);
}
}