diff --git a/lib/main.dart b/lib/main.dart index 09ecd01d..2a8fff4e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -32,7 +32,10 @@ class MyApp extends StatelessWidget { ), home: Scaffold( body: FutureBuilder( - future: context.read().init(), + future: Future.wait([ + context.read().init(), + context.read().init(), + ]), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Home( diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 6ffd0f64..e3ffda5c 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,14 +1,14 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:frosty/api/twitch_api.dart'; -import 'package:frosty/screens/video_chat.dart'; +import 'package:frosty/screens/search.dart'; +import 'package:frosty/screens/settings.dart'; import 'package:frosty/stores/auth_store.dart'; import 'package:frosty/stores/channel_list_store.dart'; import 'package:frosty/stores/home_store.dart'; -import 'package:frosty/widgets/drawer_menu.dart'; +import 'package:frosty/stores/settings_store.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; -import 'channel_list.dart'; +import '../widgets/channel_list.dart'; class Home extends StatefulWidget { final HomeStore homeStore; @@ -29,55 +29,49 @@ class _HomeState extends State { @override Widget build(BuildContext context) { - final titles = [if (context.read().isLoggedIn) 'Followed Channels', 'Top Channels', 'Categories']; + final titles = [ + if (context.read().isLoggedIn) 'Followed Channels', + 'Top Channels', + 'Categories', + 'Search', + ]; debugPrint('build home'); return Scaffold( appBar: AppBar( title: Observer( builder: (_) { - return widget.homeStore.search - ? TextField( - controller: _textController, - autocorrect: false, - autofocus: true, - decoration: const InputDecoration( - hintText: 'Search for a channel', - ), - onSubmitted: (string) async { - if (await Twitch.getUser(userLogin: string, headers: context.read().headersTwitch) != null) { - Navigator.push( - context, - CupertinoPageRoute( - builder: (_) { - return VideoChat( - userLogin: string, - ); - }, - ), - ); - } else { - const snackBar = SnackBar(content: Text('User does not exist :(')); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - } - widget.homeStore.search = false; - _textController.clear(); - }, - ) - : Text(titles[widget.homeStore.selectedIndex]); + return Text( + titles[widget.homeStore.selectedIndex], + style: GoogleFonts.daysOne(), + ); }, ), actions: [ IconButton( - icon: Observer(builder: (_) => widget.homeStore.search ? const Icon(Icons.cancel) : const Icon(Icons.search)), + icon: const Icon(Icons.settings), onPressed: () { - widget.homeStore.search = !widget.homeStore.search; - _textController.clear(); + Navigator.push( + context, + MaterialPageRoute( + fullscreenDialog: true, + builder: (context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Settings', + style: GoogleFonts.daysOne(), + ), + ), + body: Settings(settingsStore: context.read()), + ); + }, + ), + ); }, ) ], ), - drawer: const DrawerMenu(), body: Observer( builder: (_) { return IndexedStack( @@ -95,6 +89,7 @@ class _HomeState extends State { const Center( child: Text('Games'), ), + const Search(), ], ); }, @@ -103,6 +98,7 @@ class _HomeState extends State { child: Observer( builder: (_) { return BottomNavigationBar( + type: BottomNavigationBarType.fixed, items: [ if (context.read().isLoggedIn) const BottomNavigationBarItem( @@ -117,6 +113,10 @@ class _HomeState extends State { icon: Icon(Icons.gamepad), label: 'Categories', ), + const BottomNavigationBarItem( + icon: Icon(Icons.search), + label: 'Search', + ), ], currentIndex: widget.homeStore.selectedIndex, onTap: widget.homeStore.handleTap, diff --git a/lib/screens/search.dart b/lib/screens/search.dart new file mode 100644 index 00000000..3cbe6452 --- /dev/null +++ b/lib/screens/search.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:frosty/api/twitch_api.dart'; +import 'package:frosty/screens/video_chat.dart'; +import 'package:frosty/stores/auth_store.dart'; +import 'package:provider/provider.dart'; + +class Search extends StatefulWidget { + const Search({Key? key}) : super(key: key); + + @override + _SearchState createState() => _SearchState(); +} + +class _SearchState extends State { + final _textController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return TextField( + controller: _textController, + autocorrect: false, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.all(8.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + hintText: 'Search', + ), + onSubmitted: (string) async { + if (await Twitch.getUser(userLogin: string, headers: context.read().headersTwitch) != null) { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) { + return VideoChat( + userLogin: string, + ); + }, + ), + ); + } else { + const snackBar = SnackBar(content: Text('User does not exist :(')); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + _textController.clear(); + }, + ); + } +} diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 177416ab..27a3dc10 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,32 +1,30 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:frosty/stores/auth_store.dart'; import 'package:frosty/stores/settings_store.dart'; +import 'package:frosty/widgets/profile_card.dart'; import 'package:provider/provider.dart'; class Settings extends StatelessWidget { - const Settings({Key? key}) : super(key: key); + final SettingsStore settingsStore; + + const Settings({Key? key, required this.settingsStore}) : super(key: key); @override Widget build(BuildContext context) { - final settingsStore = context.read(); - - return Scaffold( - appBar: AppBar( - title: const Text('Settings'), - ), - body: Observer( - builder: (_) { - return ListView( - children: [ - SwitchListTile( - title: const Text('Enable Video'), - value: settingsStore.videoEnabled, - onChanged: (newValue) => settingsStore.videoEnabled = newValue, - ), - ], - ); - }, - ), + return Observer( + builder: (_) { + return ListView( + children: [ + ProfileCard(authStore: context.read()), + SwitchListTile( + title: const Text('Enable Video'), + value: settingsStore.videoEnabled, + onChanged: (newValue) => settingsStore.videoEnabled = newValue, + ), + ], + ); + }, ); } } diff --git a/lib/stores/home_store.dart b/lib/stores/home_store.dart index 5532eb82..23f5daa4 100644 --- a/lib/stores/home_store.dart +++ b/lib/stores/home_store.dart @@ -13,6 +13,9 @@ abstract class _HomeStoreBase with Store { @action void handleTap(int index) { + if (index == 3) { + search = !search; + } if (index != selectedIndex) { selectedIndex = index; } diff --git a/lib/stores/settings_store.dart b/lib/stores/settings_store.dart index 0397a113..73a0730d 100644 --- a/lib/stores/settings_store.dart +++ b/lib/stores/settings_store.dart @@ -1,10 +1,29 @@ +import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'settings_store.g.dart'; class SettingsStore = _SettingsStoreBase with _$SettingsStore; abstract class _SettingsStoreBase with Store { + late final SharedPreferences prefs; + late ReactionDisposer disposeSetter; + + Future init() async { + // debugPrint('Created settings provider!'); + // prefs = await SharedPreferences.getInstance(); + + // // Initialize settings from stored preferences if any. + // videoEnabled = prefs.getBool('video_enabled') ?? videoEnabled; + + // // Set up autorun to store setting anytime they're changed. + // disposeSetter = autorun((_) { + // debugPrint('video changed'); + // prefs.setBool('video_enabled', videoEnabled); + // }); + } + @observable bool videoEnabled = true; } diff --git a/lib/widgets/channel_card.dart b/lib/widgets/channel_card.dart index 9a2ba1d5..f6e5b0da 100644 --- a/lib/widgets/channel_card.dart +++ b/lib/widgets/channel_card.dart @@ -1,5 +1,4 @@ import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:frosty/models/channel.dart'; import 'package:frosty/screens/video_chat.dart'; @@ -18,7 +17,7 @@ class ChannelCard extends StatelessWidget { onTap: () { Navigator.push( context, - CupertinoPageRoute( + MaterialPageRoute( builder: (context) { return VideoChat( userLogin: channelInfo.userLogin, diff --git a/lib/screens/channel_list.dart b/lib/widgets/channel_list.dart similarity index 96% rename from lib/screens/channel_list.dart rename to lib/widgets/channel_list.dart index 60f798fa..30541aa4 100644 --- a/lib/screens/channel_list.dart +++ b/lib/widgets/channel_list.dart @@ -11,7 +11,7 @@ class ChannelList extends StatelessWidget { @override Widget build(BuildContext context) { - debugPrint('build channel list'); + debugPrint('Build channel list!'); return RefreshIndicator( child: Observer( diff --git a/lib/widgets/chat.dart b/lib/widgets/chat.dart index b5f6a532..a3ed6545 100644 --- a/lib/widgets/chat.dart +++ b/lib/widgets/chat.dart @@ -3,6 +3,7 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/screens/settings.dart'; import 'package:frosty/stores/auth_store.dart'; import 'package:frosty/stores/chat_store.dart'; +import 'package:frosty/stores/settings_store.dart'; import 'package:frosty/widgets/chat_stats.dart'; import 'package:provider/provider.dart'; @@ -50,7 +51,7 @@ class _ChatState extends State { padding: const EdgeInsets.symmetric(horizontal: 4.0), width: double.infinity, child: ElevatedButton( - onPressed: () => widget.chatStore.resumeScroll(), + onPressed: widget.chatStore.resumeScroll, child: const Text('Resume Scroll'), ), ), @@ -62,13 +63,17 @@ class _ChatState extends State { ), Row( children: [ + IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.of(context).pop(), + ), IconButton( icon: const Icon(Icons.settings), onPressed: () { showModalBottomSheet( context: context, builder: (context) { - return const Settings(); + return Settings(settingsStore: context.read()); }, ); }, diff --git a/lib/widgets/drawer_menu.dart b/lib/widgets/drawer_menu.dart deleted file mode 100644 index 39d1b3e6..00000000 --- a/lib/widgets/drawer_menu.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:frosty/screens/settings.dart'; -import 'package:frosty/widgets/profile_card.dart'; - -class DrawerMenu extends StatelessWidget { - const DrawerMenu({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Drawer( - child: ListView( - children: [ - const ProfileCard(), - ListTile( - leading: const Icon(Icons.settings), - title: const Text('Settings'), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - fullscreenDialog: true, - builder: (_) { - return const Settings(); - }, - ), - ); - }, - ) - ], - ), - ); - } -} diff --git a/lib/widgets/profile_card.dart b/lib/widgets/profile_card.dart index df982b4b..0274ad03 100644 --- a/lib/widgets/profile_card.dart +++ b/lib/widgets/profile_card.dart @@ -2,59 +2,55 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/stores/auth_store.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; class ProfileCard extends StatelessWidget { - const ProfileCard({Key? key}) : super(key: key); + final AuthStore authStore; + const ProfileCard({Key? key, required this.authStore}) : super(key: key); - @override - Widget build(BuildContext context) { - final authStore = context.read(); - - Future _showDialog() async { - return showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Logout'), - content: const Text('Are you sure you want to log out?'), - actions: [ - TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel')), - TextButton( - onPressed: () { - authStore.logout(); - Navigator.of(context).pop(); - }, - child: const Text('Yes')), - ], - ); - }, - ); - } - - return Observer( - builder: (_) { - return Center( - child: authStore.isLoggedIn - ? UserAccountsDrawerHeader( - currentAccountPicture: InkWell( - child: CircleAvatar( - foregroundImage: NetworkImage( - authStore.user!.profileImageUrl, - ), - ), - onTap: () => _showDialog(), - ), - accountName: Text(authStore.user!.displayName), - accountEmail: Text('Joined on ${DateFormat.yMMMMd('en_US').format(DateTime.parse(authStore.user!.createdAt)).toString()}'), - ) - : ElevatedButton( - onPressed: () => authStore.login(), - child: const Text('Login'), - ), + Future _showDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Logout'), + content: const Text('Are you sure you want to log out?'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel')), + TextButton( + onPressed: () { + authStore.logout(); + Navigator.of(context).pop(); + }, + child: const Text('Yes'), + ), + ], ); }, ); } + + @override + Widget build(BuildContext context) { + return Center( + child: Observer( + builder: (context) { + if (authStore.isLoggedIn) { + return ListTile( + leading: CircleAvatar( + foregroundImage: NetworkImage( + authStore.user!.profileImageUrl, + ), + ), + title: Text(authStore.user!.displayName), + onTap: () => _showDialog(context), + ); + } + return ElevatedButton( + onPressed: authStore.login, + child: const Text('Login'), + ); + }, + ), + ); + } }