diff --git a/lib/components/custom_app_bar.dart b/lib/components/custom_app_bar.dart new file mode 100644 index 0000000..47e08d2 --- /dev/null +++ b/lib/components/custom_app_bar.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class CustomAppBar extends StatefulWidget { + const CustomAppBar({super.key, required this.title}); + + final String title; + + @override + State createState() => _CustomAppBarState(); +} + +class _CustomAppBarState extends State { + @override + Widget build(BuildContext context) { + return SliverAppBar( + pinned: true, + automaticallyImplyLeading: false, + expandedHeight: 100, + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + title: Text( + widget.title, + style: + TextStyle(color: Theme.of(context).textTheme.bodyMedium!.color), + ), + ), + ); + } +} diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index fab23d4..8151acc 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/pages/app.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; @@ -22,113 +23,125 @@ class _AddAppPageState extends State { @override Widget build(BuildContext context) { SourceProvider sourceProvider = SourceProvider(); - return Center( - child: Form( - key: _formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container(), - Padding( - padding: const EdgeInsets.all(16), + return CustomScrollView(slivers: [ + const CustomAppBar(title: 'Add App'), + SliverFillRemaining( + hasScrollBody: false, + child: Center( + child: Form( + key: _formKey, child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - TextFormField( - decoration: const InputDecoration( - hintText: 'https://github.com/Author/Project', - helperText: 'Enter the App source URL'), - controller: urlInputController, - validator: (value) { - if (value == null || - value.isEmpty || - Uri.tryParse(value) == null) { - return 'Please enter a supported source URL'; - } - return null; - }, - ), + Container(), Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: ElevatedButton( - onPressed: gettingAppInfo - ? null - : () { - HapticFeedback.mediumImpact(); - if (_formKey.currentState!.validate()) { - setState(() { - gettingAppInfo = true; - }); - sourceProvider - .getApp(urlInputController.value.text) - .then((app) { - var appsProvider = - context.read(); - var settingsProvider = - context.read(); - if (appsProvider.apps.containsKey(app.id)) { - throw 'App already added'; - } - settingsProvider - .getInstallPermission() - .then((_) { - appsProvider.saveApp(app).then((_) { - urlInputController.clear(); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - AppPage(appId: app.id))); - }); - }); - }).catchError((e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toString())), - ); - }).whenComplete(() { - setState(() { - gettingAppInfo = false; - }); - }); - } - }, - child: const Text('Add'), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'https://github.com/Author/Project', + helperText: 'Enter the App source URL'), + controller: urlInputController, + validator: (value) { + if (value == null || + value.isEmpty || + Uri.tryParse(value) == null) { + return 'Please enter a supported source URL'; + } + return null; + }, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: ElevatedButton( + onPressed: gettingAppInfo + ? null + : () { + HapticFeedback.mediumImpact(); + if (_formKey.currentState!.validate()) { + setState(() { + gettingAppInfo = true; + }); + sourceProvider + .getApp( + urlInputController.value.text) + .then((app) { + var appsProvider = + context.read(); + var settingsProvider = + context.read(); + if (appsProvider.apps + .containsKey(app.id)) { + throw 'App already added'; + } + settingsProvider + .getInstallPermission() + .then((_) { + appsProvider.saveApp(app).then((_) { + urlInputController.clear(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + AppPage( + appId: app.id))); + }); + }); + }).catchError((e) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text(e.toString())), + ); + }).whenComplete(() { + setState(() { + gettingAppInfo = false; + }); + }); + } + }, + child: const Text('Add'), + ), + ), + ], ), ), + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Supported Sources:', + // style: TextStyle(fontWeight: FontWeight.bold), + // style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox( + height: 8, + ), + ...sourceProvider + .getSourceHosts() + .map((e) => GestureDetector( + onTap: () { + launchUrlString('https://$e', + mode: LaunchMode.externalApplication); + }, + child: Text( + e, + style: const TextStyle( + decoration: TextDecoration.underline, + fontStyle: FontStyle.italic), + ))) + .toList() + ]), + if (gettingAppInfo) + const LinearProgressIndicator() + else + Container(), ], - ), - ), - Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Text( - 'Supported Sources:', - // style: TextStyle(fontWeight: FontWeight.bold), - // style: Theme.of(context).textTheme.bodySmall, - ), - const SizedBox( - height: 8, - ), - ...sourceProvider - .getSourceHosts() - .map((e) => GestureDetector( - onTap: () { - launchUrlString('https://$e', - mode: LaunchMode.externalApplication); - }, - child: Text( - e, - style: const TextStyle( - decoration: TextDecoration.underline, - fontStyle: FontStyle.italic), - ))) - .toList() - ]), - if (gettingAppInfo) - const LinearProgressIndicator() - else - Container(), - ], - )), - ); + )), + )) + ]); } } diff --git a/lib/pages/app.dart b/lib/pages/app.dart index 6c1c6be..40e6c18 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -25,61 +26,63 @@ class _AppPageState extends State { appsProvider.getUpdate(app!.app.id); } return Scaffold( - appBar: AppBar( - title: Text('${app?.app.author}/${app?.app.name}'), - ), - body: settingsProvider.showAppWebpage - ? WebView( - initialUrl: app?.app.url, - javascriptMode: JavascriptMode.unrestricted, - ) - : Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - app?.app.name ?? 'App', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.displayLarge, - ), - Text( - 'By ${app?.app.author ?? 'Unknown'}', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headlineMedium, - ), - const SizedBox( - height: 32, - ), - GestureDetector( - onTap: () { - if (app?.app.url != null) { - launchUrlString(app?.app.url ?? '', - mode: LaunchMode.externalApplication); - } - }, - child: Text( - app?.app.url ?? '', + body: CustomScrollView(slivers: [ + CustomAppBar(title: '${app?.app.name}'), + SliverFillRemaining( + child: settingsProvider.showAppWebpage + ? WebView( + initialUrl: app?.app.url, + javascriptMode: JavascriptMode.unrestricted, + ) + : Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + app?.app.name ?? 'App', textAlign: TextAlign.center, - style: const TextStyle( - decoration: TextDecoration.underline, - fontStyle: FontStyle.italic, - fontSize: 12), - )), - const SizedBox( - height: 32, + style: Theme.of(context).textTheme.displayLarge, + ), + Text( + 'By ${app?.app.author ?? 'Unknown'}', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineMedium, + ), + const SizedBox( + height: 32, + ), + GestureDetector( + onTap: () { + if (app?.app.url != null) { + launchUrlString(app?.app.url ?? '', + mode: LaunchMode.externalApplication); + } + }, + child: Text( + app?.app.url ?? '', + textAlign: TextAlign.center, + style: const TextStyle( + decoration: TextDecoration.underline, + fontStyle: FontStyle.italic, + fontSize: 12), + )), + const SizedBox( + height: 32, + ), + Text( + 'Latest Version: ${app?.app.latestVersion ?? 'Unknown'}', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'Installed Version: ${app?.app.installedVersion ?? 'None'}', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], ), - Text( - 'Latest Version: ${app?.app.latestVersion ?? 'Unknown'}', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyLarge, - ), - Text( - 'Installed Version: ${app?.app.installedVersion ?? 'None'}', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyLarge, - ), - ], - ), + ), + ]), bottomSheet: Padding( padding: EdgeInsets.fromLTRB( 0, 0, 0, MediaQuery.of(context).padding.bottom), diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index 29eb35b..647fbe7 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/pages/app.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; @@ -47,34 +48,17 @@ class _AppsPageState extends State { existingUpdateAppIds, context); }); }, - icon: const Icon(Icons.update), - label: const Text('Update All')), + icon: const Icon(Icons.install_mobile_outlined), + label: const Text('Install All')), body: RefreshIndicator( onRefresh: () { HapticFeedback.lightImpact(); return appsProvider.checkUpdates(); }, child: CustomScrollView(slivers: [ - SliverAppBar( - pinned: true, - snap: false, - floating: false, - expandedHeight: 100, - backgroundColor: MaterialStateColor.resolveWith( - (states) => states.contains(MaterialState.scrolledUnder) - ? Theme.of(context).colorScheme.surface - : Theme.of(context).canvasColor, - ), - flexibleSpace: const FlexibleSpaceBar( - titlePadding: const EdgeInsets.only(bottom: 16.0, left: 20.0), - title: Text( - 'Apps', - style: TextStyle(color: Colors.black), - ), - ), - ), + const CustomAppBar(title: 'Apps'), if (appsProvider.loadingApps || appsProvider.apps.isEmpty) - SliverToBoxAdapter( + SliverFillRemaining( child: appsProvider.loadingApps ? const CircularProgressIndicator() : Text( diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 252dde1..1b48f39 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -12,31 +12,38 @@ class HomePage extends StatefulWidget { State createState() => _HomePageState(); } +class NavigationPageItem { + late String title; + late IconData icon; + late Widget widget; + + NavigationPageItem(this.title, this.icon, this.widget); +} + class _HomePageState extends State { List selectedIndexHistory = []; - List pages = [ - const AppsPage(), - const AddAppPage(), - const ImportExportPage(), - const SettingsPage() + + List pages = [ + NavigationPageItem('Apps', Icons.apps, const AppsPage()), + NavigationPageItem('Add App', Icons.add, const AddAppPage()), + NavigationPageItem( + 'Import/Export', Icons.import_export, const ImportExportPage()), + NavigationPageItem('Settings', Icons.settings, const SettingsPage()) ]; @override Widget build(BuildContext context) { return WillPopScope( child: Scaffold( - appBar: AppBar(title: const Text('Obtainium')), - body: pages.elementAt( - selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last), + body: pages + .elementAt( + selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last) + .widget, bottomNavigationBar: NavigationBar( - destinations: const [ - NavigationDestination(icon: Icon(Icons.apps), label: 'Apps'), - NavigationDestination(icon: Icon(Icons.add), label: 'Add App'), - NavigationDestination( - icon: Icon(Icons.import_export), label: 'Import/Export'), - NavigationDestination( - icon: Icon(Icons.settings), label: 'Settings'), - ], + destinations: pages + .map((e) => + NavigationDestination(icon: Icon(e.icon), label: e.title)) + .toList(), onDestinationSelected: (int index) { HapticFeedback.lightImpact(); setState(() { diff --git a/lib/pages/import_export.dart b/lib/pages/import_export.dart index 831252c..5b2b6d5 100644 --- a/lib/pages/import_export.dart +++ b/lib/pages/import_export.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/generated_form_modal.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; @@ -43,192 +44,204 @@ class _ImportExportPageState extends State { return errors; } - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedButton( - onPressed: appsProvider.apps.isEmpty || importInProgress - ? null - : () { - HapticFeedback.lightImpact(); - appsProvider.exportApps().then((String path) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Exported to $path')), - ); - }); - }, - child: const Text('Obtainium Export')), - const SizedBox( - height: 8, - ), - ElevatedButton( - onPressed: importInProgress - ? null - : () { - HapticFeedback.lightImpact(); - FilePicker.platform.pickFiles().then((result) { - setState(() { - importInProgress = true; - }); - if (result != null) { - String data = File(result.files.single.path!) - .readAsStringSync(); - try { - jsonDecode(data); - } catch (e) { - throw 'Invalid input'; - } - appsProvider.importApps(data).then((value) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - '$value App${value == 1 ? '' : 's'} Imported')), - ); - }); - } else { - // User canceled the picker - } - }).catchError((e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toString())), - ); - }).whenComplete(() { - setState(() { - importInProgress = false; - }); - }); - }, - child: const Text('Obtainium Import')), - if (importInProgress) - Column( - children: const [ - SizedBox( - height: 14, - ), - LinearProgressIndicator(), - SizedBox( - height: 14, - ), - ], - ) - else - const Divider( - height: 32, - ), - TextButton( - onPressed: importInProgress - ? null - : () { - showDialog( - context: context, - builder: (BuildContext ctx) { - return GeneratedFormModal( - title: 'Import from URL List', - items: [ - GeneratedFormItem('App URL List', true, 7) - ], - ); - }).then((values) { - if (values != null) { - var urls = (values[0] as String).split('\n'); - setState(() { - importInProgress = true; - }); - addApps(urls).then((errors) { - if (errors.isEmpty) { + return CustomScrollView(slivers: [ + const CustomAppBar(title: 'Import/Export'), + SliverFillRemaining( + hasScrollBody: false, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ElevatedButton( + onPressed: appsProvider.apps.isEmpty || importInProgress + ? null + : () { + HapticFeedback.lightImpact(); + appsProvider.exportApps().then((String path) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text('Imported ${urls.length} Apps')), + SnackBar(content: Text('Exported to $path')), ); - } else { - showDialog( - context: context, - builder: (BuildContext ctx) { - return ImportErrorDialog( - urlsLength: urls.length, - errors: errors); - }); - } - }).catchError((e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toString())), - ); - }).whenComplete(() { - setState(() { - importInProgress = false; }); - }); - } - }); - }, - child: const Text('Import from URL List')), - ...sourceProvider.massSources - .map((source) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const SizedBox(height: 8), - TextButton( - onPressed: importInProgress - ? null - : () { + }, + child: const Text('Obtainium Export')), + const SizedBox( + height: 8, + ), + ElevatedButton( + onPressed: importInProgress + ? null + : () { + HapticFeedback.lightImpact(); + FilePicker.platform.pickFiles().then((result) { + setState(() { + importInProgress = true; + }); + if (result != null) { + String data = File(result.files.single.path!) + .readAsStringSync(); + try { + jsonDecode(data); + } catch (e) { + throw 'Invalid input'; + } + appsProvider.importApps(data).then((value) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '$value App${value == 1 ? '' : 's'} Imported')), + ); + }); + } else { + // User canceled the picker + } + }).catchError((e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + }).whenComplete(() { + setState(() { + importInProgress = false; + }); + }); + }, + child: const Text('Obtainium Import')), + if (importInProgress) + Column( + children: const [ + SizedBox( + height: 14, + ), + LinearProgressIndicator(), + SizedBox( + height: 14, + ), + ], + ) + else + const Divider( + height: 32, + ), + TextButton( + onPressed: importInProgress + ? null + : () { + showDialog( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + title: 'Import from URL List', + items: [ + GeneratedFormItem( + 'App URL List', true, 7) + ], + ); + }).then((values) { + if (values != null) { + var urls = (values[0] as String).split('\n'); + setState(() { + importInProgress = true; + }); + addApps(urls).then((errors) { + if (errors.isEmpty) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + 'Imported ${urls.length} Apps')), + ); + } else { showDialog( context: context, builder: (BuildContext ctx) { - return GeneratedFormModal( - title: 'Import ${source.name}', - items: source.requiredArgs - .map((e) => - GeneratedFormItem( - e, true, 1)) - .toList()); - }).then((values) { - if (values != null) { - source.getUrls(values).then((urls) { - setState(() { - importInProgress = true; - }); - addApps(urls).then((errors) { - if (errors.isEmpty) { - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'Imported ${urls.length} Apps')), - ); - } else { - showDialog( - context: context, - builder: - (BuildContext ctx) { - return ImportErrorDialog( - urlsLength: - urls.length, - errors: errors); - }); - } - }).whenComplete(() { - setState(() { - importInProgress = false; - }); - }); - }).catchError((e) { - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text(e.toString())), - ); + return ImportErrorDialog( + urlsLength: urls.length, + errors: errors); }); - } - }); - }, - child: Text('Import ${source.name}')) - ])) - .toList() - ], - )); + } + }).catchError((e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + }).whenComplete(() { + setState(() { + importInProgress = false; + }); + }); + } + }); + }, + child: const Text('Import from URL List')), + ...sourceProvider.massSources + .map((source) => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 8), + TextButton( + onPressed: importInProgress + ? null + : () { + showDialog( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + title: + 'Import ${source.name}', + items: source.requiredArgs + .map((e) => + GeneratedFormItem( + e, true, 1)) + .toList()); + }).then((values) { + if (values != null) { + source + .getUrls(values) + .then((urls) { + setState(() { + importInProgress = true; + }); + addApps(urls).then((errors) { + if (errors.isEmpty) { + ScaffoldMessenger.of( + context) + .showSnackBar( + SnackBar( + content: Text( + 'Imported ${urls.length} Apps')), + ); + } else { + showDialog( + context: context, + builder: (BuildContext + ctx) { + return ImportErrorDialog( + urlsLength: + urls.length, + errors: errors); + }); + } + }).whenComplete(() { + setState(() { + importInProgress = false; + }); + }); + }).catchError((e) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: + Text(e.toString())), + ); + }); + } + }); + }, + child: Text('Import ${source.name}')) + ])) + .toList() + ], + ))) + ]); } } diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 6322c60..817432e 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -18,185 +19,193 @@ class _SettingsPageState extends State { if (settingsProvider.prefs == null) { settingsProvider.initializeSettings(); } - return Padding( - padding: const EdgeInsets.all(16), - child: settingsProvider.prefs == null - ? Container() - : Column( - children: [ - DropdownButtonFormField( - decoration: const InputDecoration(labelText: 'Theme'), - value: settingsProvider.theme, - items: const [ - DropdownMenuItem( - value: ThemeSettings.dark, - child: Text('Dark'), + return CustomScrollView(slivers: [ + const CustomAppBar(title: 'Add App'), + SliverFillRemaining( + hasScrollBody: true, + child: Padding( + padding: const EdgeInsets.all(16), + child: settingsProvider.prefs == null + ? Container() + : Column( + children: [ + DropdownButtonFormField( + decoration: + const InputDecoration(labelText: 'Theme'), + value: settingsProvider.theme, + items: const [ + DropdownMenuItem( + value: ThemeSettings.dark, + child: Text('Dark'), + ), + DropdownMenuItem( + value: ThemeSettings.light, + child: Text('Light'), + ), + DropdownMenuItem( + value: ThemeSettings.system, + child: Text('Follow System'), + ) + ], + onChanged: (value) { + if (value != null) { + settingsProvider.theme = value; + } + }), + const SizedBox( + height: 16, ), - DropdownMenuItem( - value: ThemeSettings.light, - child: Text('Light'), + DropdownButtonFormField( + decoration: + const InputDecoration(labelText: 'Colour'), + value: settingsProvider.colour, + items: const [ + DropdownMenuItem( + value: ColourSettings.basic, + child: Text('Obtainium'), + ), + DropdownMenuItem( + value: ColourSettings.materialYou, + child: Text('Material You'), + ) + ], + onChanged: (value) { + if (value != null) { + settingsProvider.colour = value; + } + }), + const SizedBox( + height: 16, ), - DropdownMenuItem( - value: ThemeSettings.system, - child: Text('Follow System'), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.theme = value; - } - }), - const SizedBox( - height: 16, - ), - DropdownButtonFormField( - decoration: const InputDecoration(labelText: 'Colour'), - value: settingsProvider.colour, - items: const [ - DropdownMenuItem( - value: ColourSettings.basic, - child: Text('Obtainium'), + DropdownButtonFormField( + decoration: const InputDecoration( + labelText: + 'Background Update Checking Interval'), + value: settingsProvider.updateInterval, + items: const [ + DropdownMenuItem( + value: 15, + child: Text('15 Minutes'), + ), + DropdownMenuItem( + value: 30, + child: Text('30 Minutes'), + ), + DropdownMenuItem( + value: 60, + child: Text('1 Hour'), + ), + DropdownMenuItem( + value: 360, + child: Text('6 Hours'), + ), + DropdownMenuItem( + value: 720, + child: Text('12 Hours'), + ), + DropdownMenuItem( + value: 1440, + child: Text('1 Day'), + ), + DropdownMenuItem( + value: 0, + child: Text('Never - Manual Only'), + ), + ], + onChanged: (value) { + if (value != null) { + settingsProvider.updateInterval = value; + } + }), + const SizedBox( + height: 16, ), - DropdownMenuItem( - value: ColourSettings.materialYou, - child: Text('Material You'), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.colour = value; - } - }), - const SizedBox( - height: 16, - ), - DropdownButtonFormField( - decoration: const InputDecoration( - labelText: 'Background Update Checking Interval'), - value: settingsProvider.updateInterval, - items: const [ - DropdownMenuItem( - value: 15, - child: Text('15 Minutes'), + DropdownButtonFormField( + decoration: + const InputDecoration(labelText: 'App Sort By'), + value: settingsProvider.sortColumn, + items: const [ + DropdownMenuItem( + value: SortColumnSettings.authorName, + child: Text('Author/Name'), + ), + DropdownMenuItem( + value: SortColumnSettings.nameAuthor, + child: Text('Name/Author'), + ), + DropdownMenuItem( + value: SortColumnSettings.added, + child: Text('As Added'), + ) + ], + onChanged: (value) { + if (value != null) { + settingsProvider.sortColumn = value; + } + }), + const SizedBox( + height: 16, ), - DropdownMenuItem( - value: 30, - child: Text('30 Minutes'), + DropdownButtonFormField( + decoration: const InputDecoration( + labelText: 'App Sort Order'), + value: settingsProvider.sortOrder, + items: const [ + DropdownMenuItem( + value: SortOrderSettings.ascending, + child: Text('Ascending'), + ), + DropdownMenuItem( + value: SortOrderSettings.descending, + child: Text('Descending'), + ), + ], + onChanged: (value) { + if (value != null) { + settingsProvider.sortOrder = value; + } + }), + const SizedBox( + height: 16, ), - DropdownMenuItem( - value: 60, - child: Text('1 Hour'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Show Source Webpage in App View'), + Switch( + value: settingsProvider.showAppWebpage, + onChanged: (value) { + settingsProvider.showAppWebpage = value; + }) + ], ), - DropdownMenuItem( - value: 360, - child: Text('6 Hours'), - ), - DropdownMenuItem( - value: 720, - child: Text('12 Hours'), - ), - DropdownMenuItem( - value: 1440, - child: Text('1 Day'), - ), - DropdownMenuItem( - value: 0, - child: Text('Never - Manual Only'), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton.icon( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.resolveWith( + (Set states) { + return Colors.grey; + }), + ), + onPressed: () { + HapticFeedback.lightImpact(); + launchUrlString(settingsProvider.sourceUrl, + mode: LaunchMode.externalApplication); + }, + icon: const Icon(Icons.code), + label: Text( + 'Source', + style: Theme.of(context).textTheme.bodySmall, + ), + ) + ], ), ], - onChanged: (value) { - if (value != null) { - settingsProvider.updateInterval = value; - } - }), - const SizedBox( - height: 16, - ), - DropdownButtonFormField( - decoration: - const InputDecoration(labelText: 'App Sort By'), - value: settingsProvider.sortColumn, - items: const [ - DropdownMenuItem( - value: SortColumnSettings.authorName, - child: Text('Author/Name'), - ), - DropdownMenuItem( - value: SortColumnSettings.nameAuthor, - child: Text('Name/Author'), - ), - DropdownMenuItem( - value: SortColumnSettings.added, - child: Text('As Added'), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.sortColumn = value; - } - }), - const SizedBox( - height: 16, - ), - DropdownButtonFormField( - decoration: - const InputDecoration(labelText: 'App Sort Order'), - value: settingsProvider.sortOrder, - items: const [ - DropdownMenuItem( - value: SortOrderSettings.ascending, - child: Text('Ascending'), - ), - DropdownMenuItem( - value: SortOrderSettings.descending, - child: Text('Descending'), - ), - ], - onChanged: (value) { - if (value != null) { - settingsProvider.sortOrder = value; - } - }), - const SizedBox( - height: 16, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Show Source Webpage in App View'), - Switch( - value: settingsProvider.showAppWebpage, - onChanged: (value) { - settingsProvider.showAppWebpage = value; - }) - ], - ), - const Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton.icon( - style: ButtonStyle( - foregroundColor: - MaterialStateProperty.resolveWith( - (Set states) { - return Colors.grey; - }), - ), - onPressed: () { - HapticFeedback.lightImpact(); - launchUrlString(settingsProvider.sourceUrl, - mode: LaunchMode.externalApplication); - }, - icon: const Icon(Icons.code), - label: Text( - 'Source', - style: Theme.of(context).textTheme.bodySmall, - ), - ) - ], - ), - ], - )); + ))) + ]); } }