Migrated to Material You

This commit is contained in:
Imran Remtulla
2022-08-20 16:31:52 -04:00
parent 6b43d4ed60
commit ce98c5b2ec
9 changed files with 267 additions and 157 deletions

Binary file not shown.

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:obtainium/pages/apps.dart'; import 'package:obtainium/pages/home.dart';
import 'package:obtainium/services/apps_provider.dart'; import 'package:obtainium/services/apps_provider.dart';
import 'package:obtainium/services/source_service.dart'; import 'package:obtainium/services/source_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:workmanager/workmanager.dart'; import 'package:workmanager/workmanager.dart';
import 'package:dynamic_color/dynamic_color.dart';
void backgroundUpdateCheck() { void backgroundUpdateCheck() {
Workmanager().executeTask((task, inputData) async { Workmanager().executeTask((task, inputData) async {
@ -36,6 +37,7 @@ void main() async {
await Workmanager().registerPeriodicTask( await Workmanager().registerPeriodicTask(
'update-apps-task', 'backgroundUpdateCheck', 'update-apps-task', 'backgroundUpdateCheck',
frequency: const Duration(minutes: 15), frequency: const Duration(minutes: 15),
initialDelay: const Duration(minutes: 15),
constraints: Constraints(networkType: NetworkType.connected)); constraints: Constraints(networkType: NetworkType.connected));
runApp(MultiProvider( runApp(MultiProvider(
providers: [ChangeNotifierProvider(create: (context) => AppsProvider())], providers: [ChangeNotifierProvider(create: (context) => AppsProvider())],
@ -43,16 +45,35 @@ void main() async {
)); ));
} }
var defaultThemeColour = const Color(0xFF69F0AE);
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return DynamicColorBuilder(
title: 'Obtainium', builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
theme: ThemeData( ColorScheme lightColorScheme;
primarySwatch: Colors.blue, ColorScheme darkColorScheme;
), if (lightDynamic != null && darkDynamic != null) {
home: const AppsPage()); lightColorScheme = lightDynamic.harmonized();
darkColorScheme = darkDynamic.harmonized();
} else {
lightColorScheme = ColorScheme.fromSeed(seedColor: defaultThemeColour);
darkColorScheme = ColorScheme.fromSeed(
seedColor: defaultThemeColour, brightness: Brightness.dark);
}
return MaterialApp(
title: 'Obtainium',
theme: ThemeData(
useMaterial3: true,
colorScheme: lightColorScheme,
fontFamily: 'Metropolis'),
darkTheme:
ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
home: const HomePage());
});
} }
} }

View File

@ -18,78 +18,72 @@ class _AddAppPageState extends State<AddAppPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Center(
appBar: AppBar( child: Form(
title: const Text('Obtainium - Add App'), key: _formKey,
), child: Column(
body: Center( crossAxisAlignment: CrossAxisAlignment.stretch,
child: Form( children: [
key: _formKey, const Spacer(),
child: Column( Padding(
crossAxisAlignment: CrossAxisAlignment.stretch, padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: [ child: TextFormField(
const Spacer(), decoration: const InputDecoration(
Padding( hintText: 'https://github.com/Author/Project',
padding: const EdgeInsets.symmetric(horizontal: 16.0), helperText: 'Enter the App source URL'),
child: TextFormField( controller: urlInputController,
decoration: const InputDecoration( validator: (value) {
border: OutlineInputBorder(), if (value == null ||
hintText: 'https://github.com/Author/Project', value.isEmpty ||
helperText: 'Enter the App source URL'), Uri.tryParse(value) == null) {
controller: urlInputController, return 'Please enter a supported source URL';
validator: (value) { }
if (value == null || return null;
value.isEmpty || },
Uri.tryParse(value) == null) { )),
return 'Please enter a supported source URL'; Padding(
} padding:
return null; const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
}, child: ElevatedButton(
)), onPressed: gettingAppInfo
Padding( ? null
padding: : () {
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), if (_formKey.currentState!.validate()) {
child: ElevatedButton( setState(() {
onPressed: gettingAppInfo gettingAppInfo = true;
? null });
: () { SourceService()
if (_formKey.currentState!.validate()) { .getApp(urlInputController.value.text)
.then((app) {
var appsProvider = context.read<AppsProvider>();
if (appsProvider.apps.containsKey(app.id)) {
throw 'App already added';
}
appsProvider.saveApp(app).then((_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AppPage(appId: app.id)));
});
}).catchError((e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
}).whenComplete(() {
setState(() { setState(() {
gettingAppInfo = true; gettingAppInfo = false;
}); });
SourceService() });
.getApp(urlInputController.value.text) }
.then((app) { },
var appsProvider = context.read<AppsProvider>(); child: const Text('Add'),
if (appsProvider.apps.containsKey(app.id)) {
throw 'App already added';
}
appsProvider.saveApp(app).then((_) {
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'),
),
), ),
const Spacer(), ),
if (gettingAppInfo) const LinearProgressIndicator(), const Spacer(),
], if (gettingAppInfo) const LinearProgressIndicator(),
), ],
)), ),
); ));
} }
} }

View File

@ -38,7 +38,7 @@ class _AppPageState extends State<AppPage> {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Expanded( Expanded(
child: OutlinedButton( child: ElevatedButton(
onPressed: (app?.installedVersion == null || onPressed: (app?.installedVersion == null ||
appsProvider appsProvider
.checkAppObjectForUpdate(app!)) && .checkAppObjectForUpdate(app!)) &&
@ -52,7 +52,7 @@ class _AppPageState extends State<AppPage> {
? 'Install' ? 'Install'
: 'Update'))), : 'Update'))),
const SizedBox(width: 16.0), const SizedBox(width: 16.0),
OutlinedButton( ElevatedButton(
onPressed: app?.currentDownloadId != null onPressed: app?.currentDownloadId != null
? null ? null
: () { : () {
@ -85,7 +85,8 @@ class _AppPageState extends State<AppPage> {
}); });
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Theme.of(context).errorColor), foregroundColor: Theme.of(context).errorColor,
surfaceTintColor: Theme.of(context).errorColor),
child: const Text('Remove'), child: const Text('Remove'),
), ),
])), ])),

View File

@ -17,83 +17,39 @@ class _AppsPageState extends State<AppsPage> {
var appsProvider = context.watch<AppsProvider>(); var appsProvider = context.watch<AppsProvider>();
appsProvider.getUpdates(); appsProvider.getUpdates();
return Scaffold( return Center(
appBar: AppBar( child: appsProvider.loadingApps
title: const Text('Obtainium'), ? const CircularProgressIndicator()
), : appsProvider.apps.isEmpty
body: Center( ? Text(
child: appsProvider.loadingApps 'No Apps',
? const CircularProgressIndicator() style: Theme.of(context).textTheme.headline4,
: appsProvider.apps.isEmpty )
? Text( : RefreshIndicator(
'No Apps', onRefresh: appsProvider.getUpdates,
style: Theme.of(context).textTheme.headline4, child: ListView(
) children: appsProvider.apps.values
: RefreshIndicator( .map(
onRefresh: appsProvider.getUpdates, (e) => ListTile(
child: ListView( title: Text('${e.author}/${e.name}'),
children: appsProvider.apps.values subtitle:
.map( Text(e.installedVersion ?? 'Not Installed'),
(e) => ListTile( trailing: e.installedVersion != null &&
title: Text('${e.author}/${e.name}'), e.installedVersion != e.latestVersion
subtitle: ? const Text('Update Available')
Text(e.installedVersion ?? 'Not Installed'), : null,
trailing: e.installedVersion != null && onTap: () {
e.installedVersion != e.latestVersion Navigator.push(
? const Text('Update Available') context,
: null, MaterialPageRoute(
onTap: () { builder: (context) => AppPage(appId: e.id)),
Navigator.push( );
context, },
MaterialPageRoute( ),
builder: (context) => )
AppPage(appId: e.id)), .toList(),
);
},
),
)
.toList(),
),
), ),
), ),
bottomSheet: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: appsProvider.apps.values.toList().where((e) {
return (e.installedVersion != null &&
e.installedVersion != e.latestVersion);
}).isNotEmpty
? OutlinedButton(
onPressed: () {
appsProvider.installUpdates().catchError((e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
});
},
child: const Text('Update All'))
: Container()),
const SizedBox(width: 16.0),
OutlinedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddAppPage()),
);
},
child: const Text('Add App'),
),
])),
],
),
); );
} }
} }

41
lib/pages/home.dart Normal file
View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:obtainium/pages/add_app.dart';
import 'package:obtainium/pages/apps.dart';
import 'package:obtainium/pages/settings.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int selectedIndex = 1;
List<Widget> pages = [
const SettingsPage(),
const AppsPage(),
const AddAppPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Obtainium')),
body: pages.elementAt(selectedIndex),
bottomNavigationBar: NavigationBar(
destinations: const [
NavigationDestination(
icon: Icon(Icons.settings), label: 'Settings'),
NavigationDestination(icon: Icon(Icons.apps), label: 'Apps'),
NavigationDestination(icon: Icon(Icons.add), label: 'Add App'),
],
onDestinationSelected: (int index) {
setState(() {
selectedIndex = index;
});
},
selectedIndex: selectedIndex,
));
}
}

20
lib/pages/settings.dart Normal file
View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'No Configurable Settings Yet.',
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
));
}
}

View File

@ -1,6 +1,13 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.1"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -29,6 +36,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "1.2.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -43,6 +64,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -57,6 +85,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.7" version: "0.7.7"
dynamic_color:
dependency: "direct main"
description:
name: dynamic_color
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.3"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -97,6 +132,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -149,6 +191,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.1" version: "4.0.1"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -156,6 +205,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.4" version: "0.6.4"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.6.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -413,6 +469,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.0" version: "6.1.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
sdks: sdks:
dart: ">=2.19.0-79.0.dev <3.0.0" dart: ">=2.19.0-79.0.dev <3.0.0"
flutter: ">=3.0.0" flutter: ">=3.1.0-0.0.pre.1036"

View File

@ -45,10 +45,13 @@ dependencies:
toast: ^0.3.0 toast: ^0.3.0
webview_flutter: ^3.0.4 webview_flutter: ^3.0.4
workmanager: ^0.5.0 workmanager: ^0.5.0
dynamic_color: ^1.5.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_launcher_icons: ^0.10.0
# The "flutter_lints" package below contains a set of recommended lints to # The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is # encourage good coding practices. The lint set provided by the package is
@ -57,6 +60,12 @@ dev_dependencies:
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^2.0.0 flutter_lints: ^2.0.0
flutter_icons:
android: true
image_path: "assets/icon.png"
adaptive_icon_background: "#282828"
adaptive_icon_foreground: "assets/icon.png"
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
@ -98,3 +107,8 @@ flutter:
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages # see https://flutter.dev/custom-fonts/#from-packages
fonts:
- family: Metropolis
fonts:
- asset: assets/fonts/Metropolis-Regular.otf