mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 13:26:43 +02:00
Compare commits
3 Commits
v0.2.2-bet
...
v0.2.4-bet
Author | SHA1 | Date | |
---|---|---|---|
6c1ad94b4f | |||
7d7986f8bf | |||
3ddf9ea736 |
@ -12,7 +12,7 @@ import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
|
||||
const String currentReleaseTag =
|
||||
'v0.2.2-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||
'v0.2.4-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void bgTaskCallback() {
|
||||
|
@ -23,125 +23,136 @@ class _AddAppPageState extends State<AddAppPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SourceProvider sourceProvider = SourceProvider();
|
||||
return CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Add App'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Center(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(),
|
||||
Padding(
|
||||
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.selectionClick();
|
||||
if (_formKey.currentState!.validate()) {
|
||||
setState(() {
|
||||
gettingAppInfo = true;
|
||||
});
|
||||
sourceProvider
|
||||
.getApp(
|
||||
urlInputController.value.text)
|
||||
.then((app) {
|
||||
var appsProvider =
|
||||
context.read<AppsProvider>();
|
||||
var settingsProvider =
|
||||
context.read<SettingsProvider>();
|
||||
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)));
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Add App'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Center(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(),
|
||||
Padding(
|
||||
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.selectionClick();
|
||||
if (_formKey.currentState!
|
||||
.validate()) {
|
||||
setState(() {
|
||||
gettingAppInfo = true;
|
||||
});
|
||||
});
|
||||
}).catchError((e) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(e.toString())),
|
||||
);
|
||||
}).whenComplete(() {
|
||||
setState(() {
|
||||
gettingAppInfo = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
child: const Text('Add'),
|
||||
),
|
||||
sourceProvider
|
||||
.getApp(urlInputController
|
||||
.value.text)
|
||||
.then((app) {
|
||||
var appsProvider =
|
||||
context.read<AppsProvider>();
|
||||
var settingsProvider = context
|
||||
.read<SettingsProvider>();
|
||||
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(),
|
||||
],
|
||||
)),
|
||||
))
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
@ -54,74 +54,146 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
||||
return errors;
|
||||
}
|
||||
|
||||
return CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Import/Export'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Import/Export'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: outlineButtonStyle,
|
||||
onPressed: appsProvider.apps.isEmpty ||
|
||||
importInProgress
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.selectionClick();
|
||||
appsProvider
|
||||
.exportApps()
|
||||
.then((String path) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text('Exported to $path')),
|
||||
);
|
||||
});
|
||||
},
|
||||
child: const Text('Obtainium Export'))),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: outlineButtonStyle,
|
||||
onPressed: importInProgress
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.selectionClick();
|
||||
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';
|
||||
}
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: outlineButtonStyle,
|
||||
onPressed: appsProvider.apps.isEmpty ||
|
||||
importInProgress
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.selectionClick();
|
||||
appsProvider
|
||||
.importApps(data)
|
||||
.then((value) {
|
||||
.exportApps()
|
||||
.then((String path) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'$value App${value == 1 ? '' : 's'} Imported')),
|
||||
'Exported to $path')),
|
||||
);
|
||||
});
|
||||
},
|
||||
child: const Text('Obtainium Export'))),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: outlineButtonStyle,
|
||||
onPressed: importInProgress
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.selectionClick();
|
||||
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 {
|
||||
// User canceled the picker
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext ctx) {
|
||||
return ImportErrorDialog(
|
||||
urlsLength: urls.length,
|
||||
errors: errors);
|
||||
});
|
||||
}
|
||||
}).catchError((e) {
|
||||
ScaffoldMessenger.of(context)
|
||||
@ -133,148 +205,92 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
||||
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 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
|
||||
: () {
|
||||
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) {
|
||||
},
|
||||
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(
|
||||
'Imported ${urls.length} Apps')),
|
||||
e.toString())),
|
||||
);
|
||||
} 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()
|
||||
],
|
||||
)))
|
||||
]);
|
||||
},
|
||||
child: Text('Import ${source.name}'))
|
||||
]))
|
||||
.toList()
|
||||
],
|
||||
)))
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
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';
|
||||
@ -19,214 +18,219 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
if (settingsProvider.prefs == null) {
|
||||
settingsProvider.initializeSettings();
|
||||
}
|
||||
return CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Add App'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: settingsProvider.prefs == null
|
||||
? Container()
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Appearance',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
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,
|
||||
),
|
||||
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,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: CustomScrollView(slivers: <Widget>[
|
||||
const CustomAppBar(title: 'Settings'),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: settingsProvider.prefs == null
|
||||
? Container()
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: 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(
|
||||
width: 16,
|
||||
Text(
|
||||
'Appearance',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
Expanded(
|
||||
child: 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,
|
||||
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) {
|
||||
settingsProvider.showAppWebpage = value;
|
||||
})
|
||||
],
|
||||
),
|
||||
const Divider(
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
'More',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
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 Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
style: ButtonStyle(
|
||||
foregroundColor:
|
||||
MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
return Colors.grey;
|
||||
if (value != null) {
|
||||
settingsProvider.theme = value;
|
||||
}
|
||||
}),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrlString(settingsProvider.sourceUrl,
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
icon: const Icon(Icons.code),
|
||||
label: Text(
|
||||
'Source',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
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,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: 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(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: 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 Divider(
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
'More',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
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 Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
style: ButtonStyle(
|
||||
foregroundColor:
|
||||
MaterialStateProperty.resolveWith<
|
||||
Color>((Set<MaterialState> states) {
|
||||
return Colors.grey;
|
||||
}),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrlString(settingsProvider.sourceUrl,
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
icon: const Icon(Icons.code),
|
||||
label: Text(
|
||||
'Source',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)))
|
||||
]);
|
||||
)))
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.2.2+13 # When changing this, update the tag in main() accordingly
|
||||
version: 0.2.4+15 # When changing this, update the tag in main() accordingly
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
||||
|
Reference in New Issue
Block a user