diff --git a/analysis_options.yaml b/analysis_options.yaml index cc50fe2..e2f34ae 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -5,6 +5,7 @@ linter: public_member_api_docs: false prefer_expression_function_bodies: true prefer_single_quotes: true + prefer_final_locals: true analyzer: exclude: - - '**/*.g.dart' + - "**/*.g.dart" diff --git a/lib/comment_tree.dart b/lib/comment_tree.dart index f8d6630..671b7e9 100644 --- a/lib/comment_tree.dart +++ b/lib/comment_tree.dart @@ -21,7 +21,7 @@ class CommentTree { static List fromList(List comments) { CommentTree gatherChildren(CommentTree parent) { - for (var el in comments) { + for (final el in comments) { if (el.parentId == parent.comment.id) { parent.children.add(gatherChildren(CommentTree(el))); } @@ -29,7 +29,7 @@ class CommentTree { return parent; } - var parents = []; + final parents = []; // first pass to get all the parents for (var i = 0; i < comments.length; i++) { @@ -38,7 +38,7 @@ class CommentTree { } } - var result = parents.map(gatherChildren).toList(); + final result = parents.map(gatherChildren).toList(); return result; } @@ -63,7 +63,7 @@ class CommentTree { void _sort(int compare(CommentTree a, CommentTree b)) { children.sort(compare); - for (var el in children) { + for (final el in children) { el._sort(compare); } } diff --git a/lib/hooks/delayed_loading.dart b/lib/hooks/delayed_loading.dart index 9280e21..781ad61 100644 --- a/lib/hooks/delayed_loading.dart +++ b/lib/hooks/delayed_loading.dart @@ -23,9 +23,9 @@ class DelayedLoading { /// and loading is triggered after [delayDuration]. /// Everything can be reset with [.cancel()] DelayedLoading useDelayedLoading(Duration delayDuration) { - var loading = useState(false); - var pending = useState(false); - var timerHandle = useRef(null); + final loading = useState(false); + final pending = useState(false); + final timerHandle = useRef(null); return DelayedLoading( loading: loading.value, diff --git a/lib/hooks/stores.dart b/lib/hooks/stores.dart new file mode 100644 index 0000000..85b9287 --- /dev/null +++ b/lib/hooks/stores.dart @@ -0,0 +1,8 @@ +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:provider/provider.dart'; + +import '../stores/accounts_store.dart'; +import '../stores/config_store.dart'; + +AccountsStore useAccountsStore() => useContext().watch(); +ConfigStore useConfigStore() => useContext().watch(); diff --git a/lib/main.dart b/lib/main.dart index bc4056b..0062dbb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,23 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:provider/provider.dart'; +import 'hooks/stores.dart'; +import 'pages/profile_tab.dart'; import 'stores/accounts_store.dart'; import 'stores/config_store.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - var configStore = ConfigStore(); + final configStore = ConfigStore(); await configStore.load(); - var accountsStore = AccountsStore(); + final accountsStore = AccountsStore(); await accountsStore.load(); runApp( @@ -31,71 +37,51 @@ Future main() async { ); } -class MyApp extends StatelessWidget { +class MyApp extends HookWidget { @override - Widget build(BuildContext context) => Observer( - builder: (ctx) { - var maybeAmoledColor = - ctx.watch().amoledDarkMode ? Colors.black : null; + Widget build(BuildContext context) { + final configStore = useConfigStore(); - return MaterialApp( - title: 'Flutter Demo', - themeMode: ctx.watch().theme, - darkTheme: ThemeData.dark().copyWith( - scaffoldBackgroundColor: maybeAmoledColor, - backgroundColor: maybeAmoledColor, - canvasColor: maybeAmoledColor, - cardColor: maybeAmoledColor, - splashColor: maybeAmoledColor, - ), - theme: ThemeData( - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: MyHomePage(title: 'Flutter hello world'), - ); - }, - ); -} + return Observer( + builder: (ctx) { + final maybeAmoledColor = + configStore.amoledDarkMode ? Colors.black : null; -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], + return MaterialApp( + title: 'Lemmur', + themeMode: configStore.theme, + darkTheme: ThemeData.dark().copyWith( + scaffoldBackgroundColor: maybeAmoledColor, + backgroundColor: maybeAmoledColor, + canvasColor: maybeAmoledColor, + cardColor: maybeAmoledColor, + splashColor: maybeAmoledColor, ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), - ); + theme: ThemeData( + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(), + ); + }, + ); + } +} + +class MyHomePage extends HookWidget { + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + useEffect(() { + Future.microtask( + () => SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + systemNavigationBarColor: theme.scaffoldBackgroundColor, + )), + ); + + return null; + }, [theme.scaffoldBackgroundColor]); + + return UserProfileTab(); + } } diff --git a/lib/pages/communities_tab.dart b/lib/pages/communities_tab.dart index 7db66fa..e226936 100644 --- a/lib/pages/communities_tab.dart +++ b/lib/pages/communities_tab.dart @@ -5,10 +5,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fuzzy/fuzzy.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; -import 'package:provider/provider.dart'; import '../hooks/delayed_loading.dart'; -import '../stores/accounts_store.dart'; +import '../hooks/memo_future.dart'; +import '../hooks/stores.dart'; import '../util/extensions/iterators.dart'; import '../util/text_color.dart'; @@ -17,23 +17,18 @@ class CommunitiesTab extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); - var filterController = useTextEditingController(); + final theme = Theme.of(context); + final filterController = useTextEditingController(); useValueListenable(filterController); - var amountOfDisplayInstances = useMemoized(() { - var accountsStore = context.watch(); + final accountsStore = useAccountsStore(); - return accountsStore.users.keys - .where((e) => !accountsStore.isAnonymousFor(e)) - .length; - }); - var isCollapsed = useState(List.filled(amountOfDisplayInstances, false)); + final amountOfDisplayInstances = useMemoized(() => accountsStore.users.keys + .where((e) => !accountsStore.isAnonymousFor(e)) + .length); + final isCollapsed = useState(List.filled(amountOfDisplayInstances, false)); - // TODO: use useMemoFuture - var instancesFut = useMemoized(() { - var accountsStore = context.watch(); - - var futures = accountsStore.users.keys + final instancesSnap = useMemoFuture(() { + final futures = accountsStore.users.keys .where((e) => !accountsStore.isAnonymousFor(e)) .map( (instanceUrl) => @@ -43,10 +38,8 @@ class CommunitiesTab extends HookWidget { return Future.wait(futures); }); - var communitiesFut = useMemoized(() { - var accountsStore = context.watch(); - - var futures = accountsStore.users.keys + final communitiesSnap = useMemoFuture(() { + final futures = accountsStore.users.keys .where((e) => !accountsStore.isAnonymousFor(e)) .map( (instanceUrl) => LemmyApi(instanceUrl) @@ -63,9 +56,6 @@ class CommunitiesTab extends HookWidget { return Future.wait(futures); }); - var communitiesSnap = useFuture(communitiesFut); - var instancesSnap = useFuture(instancesFut); - if (communitiesSnap.hasError || instancesSnap.hasError) { return Scaffold( appBar: AppBar(), @@ -93,12 +83,12 @@ class CommunitiesTab extends HookWidget { ); } - var instances = instancesSnap.data; - var communities = communitiesSnap.data + final instances = instancesSnap.data; + final communities = communitiesSnap.data ..forEach( (e) => e.sort((a, b) => a.communityName.compareTo(b.communityName))); - var filterIcon = () { + final filterIcon = () { if (filterController.text.isEmpty) { return Icon(Icons.filter_list); } @@ -113,7 +103,7 @@ class CommunitiesTab extends HookWidget { }(); filterCommunities(List comm) { - var matches = Fuzzy( + final matches = Fuzzy( comm.map((e) => e.communityName).toList(), options: FuzzyOptions(threshold: 0.5), ).search(filterController.text).map((e) => e.item); @@ -146,7 +136,7 @@ class CommunitiesTab extends HookWidget { ), body: ListView( children: [ - for (var i in Iterable.generate(amountOfDisplayInstances)) + for (final i in Iterable.generate(amountOfDisplayInstances)) Column( children: [ ListTile( @@ -178,7 +168,7 @@ class CommunitiesTab extends HookWidget { ), ), if (!isCollapsed.value[i]) - for (var comm in filterCommunities(communities[i])) + for (final comm in filterCommunities(communities[i])) Padding( padding: const EdgeInsets.only(left: 17), child: ListTile( @@ -236,9 +226,10 @@ class _CommunitySubscribeToggle extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); - var subbed = useState(true); - var delayed = useDelayedLoading(const Duration(milliseconds: 500)); + final theme = Theme.of(context); + final subbed = useState(true); + final delayed = useDelayedLoading(const Duration(milliseconds: 500)); + final accountsStore = useAccountsStore(); handleTap() async { delayed.start(); @@ -247,10 +238,7 @@ class _CommunitySubscribeToggle extends HookWidget { await LemmyApi(instanceUrl).v1.followCommunity( communityId: communityId, follow: !subbed.value, - auth: context - .read() - .defaultTokenFor(instanceUrl) - .raw, + auth: accountsStore.defaultTokenFor(instanceUrl).raw, ); subbed.value = !subbed.value; } on Exception catch (err) { diff --git a/lib/pages/community.dart b/lib/pages/community.dart index b5fc289..2838253 100644 --- a/lib/pages/community.dart +++ b/lib/pages/community.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:cached_network_image/cached_network_image.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:flutter/gestures.dart'; @@ -7,11 +5,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:intl/intl.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; -import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart' as ul; +import '../hooks/delayed_loading.dart'; import '../hooks/memo_future.dart'; -import '../stores/accounts_store.dart'; +import '../hooks/stores.dart'; import '../util/api_extensions.dart'; import '../util/goto.dart'; import '../util/intl.dart'; @@ -49,8 +47,10 @@ class CommunityPage extends HookWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - var fullCommunitySnap = useMemoFuture(() { - final token = context.watch().defaultTokenFor(instanceUrl); + final accountsStore = useAccountsStore(); + + final fullCommunitySnap = useMemoFuture(() { + final token = accountsStore.defaultTokenFor(instanceUrl); if (communityId != null) { return LemmyApi(instanceUrl).v1.getCommunity( @@ -535,13 +535,11 @@ class _FollowButton extends HookWidget { final theme = Theme.of(context); final isSubbed = useState(community.subscribed ?? false); - final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor); - final token = - context.watch().defaultTokenFor(community.instanceUrl); + final token = useAccountsStore().defaultTokenFor(community.instanceUrl); - // TODO: use hook for handling spinner and pending - final showSpinner = useState(false); - final isPending = useState(false); + final delayed = useDelayedLoading(const Duration(milliseconds: 500)); + + final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor); subscribe() async { if (token == null) { @@ -550,13 +548,10 @@ class _FollowButton extends HookWidget { return; } - isPending.value = true; - var spinnerTimer = - Timer(Duration(milliseconds: 500), () => showSpinner.value = true); + delayed.start(); - final api = LemmyApi(community.instanceUrl).v1; try { - await api.followCommunity( + await LemmyApi(community.instanceUrl).v1.followCommunity( communityId: community.id, follow: !isSubbed.value, auth: token?.raw); @@ -574,17 +569,14 @@ class _FollowButton extends HookWidget { )); } - // clean up - spinnerTimer.cancel(); - isPending.value = false; - showSpinner.value = false; + delayed.cancel(); } return Center( child: SizedBox( height: 27, width: 160, - child: showSpinner.value + child: delayed.loading ? RaisedButton( onPressed: null, child: SizedBox( @@ -598,7 +590,7 @@ class _FollowButton extends HookWidget { ) : RaisedButton.icon( padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20), - onPressed: isPending.value ? () {} : subscribe, + onPressed: delayed.pending ? () {} : subscribe, icon: isSubbed.value ? Icon(Icons.remove, size: 18, color: colorOnTopOfAccent) : Icon(Icons.add, size: 18, color: colorOnTopOfAccent), diff --git a/lib/pages/instance.dart b/lib/pages/instance.dart index d8ec37f..0877956 100644 --- a/lib/pages/instance.dart +++ b/lib/pages/instance.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:intl/intl.dart'; @@ -272,10 +273,11 @@ class _AboutTab extends HookWidget { } void goToBannedUsers(BuildContext c) { - Navigator.of(c).push(MaterialPageRoute( - builder: (_) => UsersListPage( + goTo( + c, + (_) => UsersListPage( users: site.banned.reversed.toList(), title: 'Banned users'), - )); + ); } @override @@ -284,11 +286,12 @@ class _AboutTab extends HookWidget { final commSnap = useFuture(communitiesFuture); void goToCommunities() { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => CommunitiesListPage( + goTo( + context, + (_) => CommunitiesListPage( communities: commSnap.data, title: 'Communities of ${site.site.name}'), - )); + ); } return SingleChildScrollView( diff --git a/lib/pages/profile_tab.dart b/lib/pages/profile_tab.dart index 0d6a2c7..3142b42 100644 --- a/lib/pages/profile_tab.dart +++ b/lib/pages/profile_tab.dart @@ -1,10 +1,11 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:provider/provider.dart'; -import '../stores/accounts_store.dart'; +import '../hooks/stores.dart'; import '../util/api_extensions.dart'; +import '../util/goto.dart'; import '../widgets/bottom_modal.dart'; import '../widgets/user_profile.dart'; import 'settings.dart'; @@ -14,11 +15,12 @@ class UserProfileTab extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); + final accountsStore = useAccountsStore(); return Observer( builder: (ctx) { - if (ctx.watch().hasNoAccount) { + if (accountsStore.hasNoAccount) { return Scaffold( body: Center( child: Column( @@ -27,8 +29,7 @@ class UserProfileTab extends HookWidget { Text('No account was added.'), FlatButton.icon( onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => AccountsConfigPage())); + goTo(context, (_) => AccountsConfigPage()); }, icon: Icon(Icons.add), label: Text('Add account'), @@ -39,7 +40,7 @@ class UserProfileTab extends HookWidget { ); } - var user = ctx.watch().defaultUser; + final user = accountsStore.defaultUser; return Scaffold( extendBodyBehindAppBar: true, @@ -68,12 +69,9 @@ class UserProfileTab extends HookWidget { context: context, backgroundColor: Colors.transparent, builder: (_) { - var userTags = []; + final userTags = []; - ctx - .read() - .users - .forEach((instanceUrl, value) { + accountsStore.users.forEach((instanceUrl, value) { value.forEach((username, _) { userTags.add('$username@$instanceUrl'); }); @@ -81,8 +79,8 @@ class UserProfileTab extends HookWidget { return Observer( builder: (ctx) { - var user = ctx.watch().defaultUser; - var instanceUrl = user.instanceUrl; + final user = accountsStore.defaultUser; + final instanceUrl = user.instanceUrl; return BottomModal( title: 'account', @@ -94,8 +92,8 @@ class UserProfileTab extends HookWidget { title: Text(tag), groupValue: '${user.name}@$instanceUrl', onChanged: (selected) { - var userTag = selected.split('@'); - ctx.read().setDefaultAccount( + final userTag = selected.split('@'); + accountsStore.setDefaultAccount( userTag[1], userTag[0]); Navigator.of(ctx).pop(); }, @@ -113,8 +111,7 @@ class UserProfileTab extends HookWidget { IconButton( icon: Icon(Icons.settings), onPressed: () { - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => SettingsPage())); + goTo(context, (_) => SettingsPage()); }, ) ], diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 6ac555e..b5c9181 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,15 +1,15 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:provider/provider.dart'; -import '../stores/accounts_store.dart'; -import '../stores/config_store.dart'; +import '../hooks/stores.dart'; +import '../util/goto.dart'; class SettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); return Scaffold( appBar: AppBar( @@ -26,16 +26,14 @@ class SettingsPage extends StatelessWidget { leading: Icon(Icons.person), title: Text('Accounts'), onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => AccountsConfigPage())); + goTo(context, (_) => AccountsConfigPage()); }, ), ListTile( leading: Icon(Icons.color_lens), title: Text('Appearance'), onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => AppearanceConfigPage())); + goTo(context, (_) => AppearanceConfigPage()); }, ) ], @@ -45,10 +43,11 @@ class SettingsPage extends StatelessWidget { } } -class AppearanceConfigPage extends StatelessWidget { +class AppearanceConfigPage extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); + final configStore = useConfigStore(); return Scaffold( appBar: AppBar( @@ -66,16 +65,16 @@ class AppearanceConfigPage extends StatelessWidget { RadioListTile( value: theme, title: Text(theme.toString().split('.')[1]), - groupValue: ctx.watch().theme, + groupValue: configStore.theme, onChanged: (selected) { - ctx.read().theme = selected; + configStore.theme = selected; }, ), SwitchListTile( title: Text('AMOLED dark mode'), - value: ctx.watch().amoledDarkMode, + value: configStore.amoledDarkMode, onChanged: (checked) { - ctx.read().amoledDarkMode = checked; + configStore.amoledDarkMode = checked; }) ], ), @@ -89,7 +88,8 @@ class AccountsConfigPage extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); + final accountsStore = useAccountsStore(); return Scaffold( key: _scaffoldKey, @@ -112,14 +112,13 @@ class AccountsConfigPage extends HookWidget { ), body: Observer( builder: (ctx) { - var accountsStore = ctx.watch(); - var theme = Theme.of(context); + final theme = Theme.of(context); return ListView( children: [ - for (var entry in accountsStore.users.entries) ...[ + for (final entry in accountsStore.users.entries) ...[ _SectionHeading(entry.key), - for (var username in entry.value.keys) ...[ + for (final username in entry.value.keys) ...[ ListTile( trailing: username == accountsStore.defaultUserFor(entry.key).name @@ -166,17 +165,16 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { @override Widget build(BuildContext context) { - var instanceController = useTextEditingController(); + final instanceController = useTextEditingController(); useValueListenable(instanceController); + final accountsStore = useAccountsStore(); - var loading = useState(false); + final loading = useState(false); handleOnAdd() async { try { loading.value = true; - await context - .read() - .addInstance(instanceController.text); + await accountsStore.addInstance(instanceController.text); scaffoldKey.currentState.hideCurrentSnackBar(); } on Exception catch (err) { scaffoldKey.currentState.showSnackBar(SnackBar( @@ -222,21 +220,22 @@ class _AccountsConfigAddAccountDialog extends HookWidget { @override Widget build(BuildContext context) { - var usernameController = useTextEditingController(); - var passwordController = useTextEditingController(); + final usernameController = useTextEditingController(); + final passwordController = useTextEditingController(); useValueListenable(usernameController); useValueListenable(passwordController); + final accountsStore = useAccountsStore(); - var loading = useState(false); + final loading = useState(false); handleOnAdd() async { try { loading.value = true; - await context.read().addAccount( - instanceUrl, - usernameController.text, - passwordController.text, - ); + await accountsStore.addAccount( + instanceUrl, + usernameController.text, + passwordController.text, + ); } on Exception catch (err) { scaffoldKey.currentState.showSnackBar(SnackBar( content: Text(err.toString()), diff --git a/lib/pages/user.dart b/lib/pages/user.dart index ef3a3cd..270c4d3 100644 --- a/lib/pages/user.dart +++ b/lib/pages/user.dart @@ -30,9 +30,9 @@ class UserPage extends HookWidget { @override Widget build(BuildContext context) { - var userViewSnap = useFuture(_userView); + final userViewSnap = useFuture(_userView); - var body = () { + final body = () { if (userViewSnap.hasData) { return UserProfile.fromUserView(userViewSnap.data); } else if (userViewSnap.hasError) { diff --git a/lib/stores/accounts_store.dart b/lib/stores/accounts_store.dart index 601f569..40a4282 100644 --- a/lib/stores/accounts_store.dart +++ b/lib/stores/accounts_store.dart @@ -14,7 +14,6 @@ abstract class _AccountsStore with Store { _AccountsStore() { // persistently save settings each time they are changed _saveReactionDisposer = reaction( - // TODO: does not react to deep changes in users and tokens (_) => [ users.forEach((k, submap) => MapEntry(k, submap.forEach((k2, v2) => MapEntry(k2, v2)))), @@ -32,7 +31,7 @@ abstract class _AccountsStore with Store { } void load() async { - var prefs = await SharedPreferences.getInstance(); + final prefs = await SharedPreferences.getInstance(); nestedMapsCast(String key, T f(Map json)) => ObservableMap.of( @@ -59,7 +58,7 @@ abstract class _AccountsStore with Store { } void save() async { - var prefs = await SharedPreferences.getInstance(); + final prefs = await SharedPreferences.getInstance(); await prefs.setString('defaultAccount', _defaultAccount); await prefs.setString('defaultAccounts', jsonEncode(_defaultAccounts)); @@ -93,7 +92,7 @@ abstract class _AccountsStore with Store { return null; } - var userTag = _defaultAccount.split('@'); + final userTag = _defaultAccount.split('@'); return users[userTag[1]][userTag[0]]; } @@ -103,7 +102,7 @@ abstract class _AccountsStore with Store { return null; } - var userTag = _defaultAccount.split('@'); + final userTag = _defaultAccount.split('@'); return tokens[userTag[1]][userTag[0]]; } @@ -159,13 +158,13 @@ abstract class _AccountsStore with Store { throw Exception('No such instance was added'); } - var lemmy = LemmyApi(instanceUrl).v1; + final lemmy = LemmyApi(instanceUrl).v1; - var token = await lemmy.login( + final token = await lemmy.login( usernameOrEmail: usernameOrEmail, password: password, ); - var userData = + final userData = await lemmy.getSite(auth: token.raw).then((value) => value.myUser); // first account for this instance diff --git a/lib/stores/config_store.dart b/lib/stores/config_store.dart index 9aafcd4..bb6b34a 100644 --- a/lib/stores/config_store.dart +++ b/lib/stores/config_store.dart @@ -22,14 +22,14 @@ abstract class _ConfigStore with Store { } void load() async { - var prefs = await SharedPreferences.getInstance(); + final prefs = await SharedPreferences.getInstance(); // load saved settings or create defaults theme = _themeModeFromString(prefs.getString('theme') ?? 'system'); amoledDarkMode = prefs.getBool('amoledDarkMode') ?? false; } void save() async { - var prefs = await SharedPreferences.getInstance(); + final prefs = await SharedPreferences.getInstance(); await prefs.setString('theme', describeEnum(theme)); await prefs.setBool('amoledDarkMode', amoledDarkMode); diff --git a/lib/url_launcher.dart b/lib/url_launcher.dart index b6ab848..4f520b4 100644 --- a/lib/url_launcher.dart +++ b/lib/url_launcher.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart' as ul; @@ -7,6 +8,7 @@ import 'pages/full_post.dart'; import 'pages/instance.dart'; import 'pages/user.dart'; import 'stores/accounts_store.dart'; +import 'util/goto.dart'; Future linkLauncher({ @required BuildContext context, @@ -14,7 +16,7 @@ Future linkLauncher({ @required String instanceUrl, }) async { push(Widget Function() builder) { - Navigator.of(context).push(MaterialPageRoute(builder: (c) => builder())); + goTo(context, (c) => builder()); } final instances = context.read().users.keys.toList(); diff --git a/lib/util/goto.dart b/lib/util/goto.dart index 885eeaa..2f7f0c7 100644 --- a/lib/util/goto.dart +++ b/lib/util/goto.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../pages/community.dart'; @@ -5,41 +6,46 @@ import '../pages/full_post.dart'; import '../pages/instance.dart'; import '../pages/user.dart'; -void goToInstance(BuildContext context, String instanceUrl) => - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => InstancePage(instanceUrl: instanceUrl), +Future goTo( + BuildContext context, + Widget Function(BuildContext context) builder, +) => + Navigator.of(context).push(CupertinoPageRoute( + builder: builder, )); +void goToInstance(BuildContext context, String instanceUrl) => + goTo(context, (context) => InstancePage(instanceUrl: instanceUrl)); + // ignore: camel_case_types abstract class goToCommunity { /// Navigates to `CommunityPage` static void byId(BuildContext context, String instanceUrl, int communityId) => - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => CommunityPage.fromId( + goTo( + context, + (context) => CommunityPage.fromId( instanceUrl: instanceUrl, communityId: communityId), - )); + ); static void byName( BuildContext context, String instanceUrl, String communityName) => - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => CommunityPage.fromName( + goTo( + context, + (context) => CommunityPage.fromName( instanceUrl: instanceUrl, communityName: communityName), - )); + ); } // ignore: camel_case_types abstract class goToUser { static void byId(BuildContext context, String instanceUrl, int userId) => - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - UserPage(instanceUrl: instanceUrl, userId: userId))); + goTo(context, + (context) => UserPage(instanceUrl: instanceUrl, userId: userId)); static void byName( BuildContext context, String instanceUrl, String userName) => throw UnimplementedError('need to create UserProfile constructor first'); } -void goToPost(BuildContext context, String instanceUrl, int postId) => - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - FullPostPage(instanceUrl: instanceUrl, id: postId))); +void goToPost(BuildContext context, String instanceUrl, int postId) => goTo( + context, (context) => FullPostPage(instanceUrl: instanceUrl, id: postId)); diff --git a/lib/widgets/badge.dart b/lib/widgets/badge.dart index fffe847..3b9b027 100644 --- a/lib/widgets/badge.dart +++ b/lib/widgets/badge.dart @@ -11,7 +11,7 @@ class Badge extends StatelessWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); return Container( height: 25, diff --git a/lib/widgets/bottom_modal.dart b/lib/widgets/bottom_modal.dart index c9abb75..50f13d2 100644 --- a/lib/widgets/bottom_modal.dart +++ b/lib/widgets/bottom_modal.dart @@ -8,7 +8,7 @@ class BottomModal extends StatelessWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); return SafeArea( child: Padding( diff --git a/lib/widgets/comment.dart b/lib/widgets/comment.dart index 9d71bb8..559be00 100644 --- a/lib/widgets/comment.dart +++ b/lib/widgets/comment.dart @@ -314,7 +314,7 @@ class Comment extends HookWidget { : BorderSide.none, top: BorderSide(width: 0.2))), ), - for (var c in commentTree.children) + for (final c in commentTree.children) Comment( c, indent: indent + 1, diff --git a/lib/widgets/comment_section.dart b/lib/widgets/comment_section.dart index 95b19ba..297db74 100644 --- a/lib/widgets/comment_section.dart +++ b/lib/widgets/comment_section.dart @@ -34,9 +34,9 @@ class CommentSection extends HookWidget { @override Widget build(BuildContext context) { - var sorting = useState(sortType); - var rawComms = useState(rawComments); - var comms = useState(comments); + final sorting = useState(sortType); + final rawComms = useState(rawComments); + final comms = useState(comments); void sortComments(CommentSortType sort) { if (sort != sorting.value && sort != CommentSortType.chat) { @@ -106,7 +106,8 @@ class CommentSection extends HookWidget { postCreatorId: postCreatorId, ) else - for (var com in comms.value) Comment(com, postCreatorId: postCreatorId), + for (final com in comms.value) + Comment(com, postCreatorId: postCreatorId), ]); } } diff --git a/lib/widgets/fullscreenable_image.dart b/lib/widgets/fullscreenable_image.dart index 5f23506..2fb28fb 100644 --- a/lib/widgets/fullscreenable_image.dart +++ b/lib/widgets/fullscreenable_image.dart @@ -1,6 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../pages/media_view.dart'; +import '../util/goto.dart'; class FullscreenableImage extends StatelessWidget { final String url; @@ -13,9 +15,7 @@ class FullscreenableImage extends StatelessWidget { }) : super(key: key); _onTap(BuildContext c) { - Navigator.of(c).push(MaterialPageRoute( - builder: (context) => MediaViewPage(url), - )); + goTo(c, (context) => MediaViewPage(url)); } @override diff --git a/lib/widgets/post.dart b/lib/widgets/post.dart index 40614a9..1fb4c75 100644 --- a/lib/widgets/post.dart +++ b/lib/widgets/post.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -150,7 +151,7 @@ class Post extends StatelessWidget { final urlDomain = () { if (post.url == null) return null; - var url = post.url.split('/')[2]; + final url = post.url.split('/')[2]; if (url.startsWith('www.')) return url.substring(4); return url; }(); @@ -425,9 +426,10 @@ class Post extends StatelessWidget { child: InkWell( onTap: fullPost ? null - : () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => FullPostPage.fromPostView( - post))), //, instanceUrl, post.id), + : () => goTo( + context, + (context) => + FullPostPage.fromPostView(post)), //, instanceUrl, post.id), child: Column( children: [ info(), diff --git a/lib/widgets/post_list_options.dart b/lib/widgets/post_list_options.dart index 4975c09..11f4a29 100644 --- a/lib/widgets/post_list_options.dart +++ b/lib/widgets/post_list_options.dart @@ -16,7 +16,7 @@ class PostListOptions extends HookWidget { @override Widget build(BuildContext context) { - var sort = useState(defaultSort); + final sort = useState(defaultSort); void selectSortType(BuildContext context) { showModalBottomSheet( @@ -27,7 +27,7 @@ class PostListOptions extends HookWidget { title: 'sort by', child: Column( children: [ - for (var x in SortType.values) + for (final x in SortType.values) RadioListTile( value: x, groupValue: sort.value, diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index f5afc3a..d5ebef2 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -29,13 +29,13 @@ class UserProfile extends HookWidget { @override Widget build(BuildContext context) { - var theme = Theme.of(context); + final theme = Theme.of(context); final colorOnTopOfAccentColor = textColorBasedOnBackground(theme.accentColor); - var userViewSnap = useFuture(_userView, preserveState: false); + final userViewSnap = useFuture(_userView, preserveState: false); - Widget bio = () { + final bio = () { if (userViewSnap.hasData) { if (userViewSnap.data.bio != null) { return Padding(