refactor: extract theme model from settings

This commit is contained in:
Rongjian Zhang 2019-09-02 21:52:32 +08:00
parent 6c6233cbcb
commit a67b389f72
18 changed files with 293 additions and 258 deletions

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/repo.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
@ -25,6 +26,15 @@ class _HomeState extends State<Home> {
int active = 0;
// String login;
@override
void initState() {
super.initState();
nextTick(() {
// FIXME:
Provider.of<ThemeModel>(context).init();
});
}
Widget _buildNotificationIcon(BuildContext context) {
int count = Provider.of<NotificationModel>(context).count;
if (count == 0) {
@ -106,7 +116,7 @@ class _HomeState extends State<Home> {
);
// TODO:
if (!settings.ready) {
if (!settings.ready || !Provider.of<ThemeModel>(context).ready) {
return MaterialApp(theme: themData, home: Scaffold(body: Text('a')));
}
@ -115,7 +125,7 @@ class _HomeState extends State<Home> {
return MaterialApp(theme: themData, home: LoginScreen());
}
switch (settings.theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoApp(
home: CupertinoTheme(
@ -163,6 +173,7 @@ class App extends StatelessWidget {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => NotificationModel()),
ChangeNotifierProvider(builder: (context) => ThemeModel()),
],
child: SettingsProvider(child: Home()),
);

184
lib/models/theme.dart Normal file
View File

@ -0,0 +1,184 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class DialogOption<T> {
final T value;
final Widget widget;
DialogOption({this.value, this.widget});
}
class ThemeMap {
static const material = 0;
static const cupertino = 1;
static const values = [ThemeMap.material, ThemeMap.cupertino];
}
class ThemeModel with ChangeNotifier {
static const storageKey = 'theme';
int _theme;
int get theme => _theme;
bool get ready => _theme != null;
init() async {
var prefs = await SharedPreferences.getInstance();
int v = prefs.getInt(storageKey);
print('read theme: $v');
if (ThemeMap.values.contains(v)) {
_theme = v;
} else if (Platform.isIOS) {
_theme = ThemeMap.cupertino;
} else {
_theme = ThemeMap.material;
}
notifyListeners();
}
Future<void> setTheme(int v) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_theme = v;
await prefs.setInt(storageKey, v);
print('write theme: $v');
notifyListeners();
}
void pushRoute({
@required BuildContext context,
@required WidgetBuilder builder,
bool fullscreenDialog = false,
}) {
switch (theme) {
case ThemeMap.cupertino:
Navigator.of(context).push(CupertinoPageRoute(
builder: builder,
fullscreenDialog: fullscreenDialog,
));
break;
default:
Navigator.of(context).push(MaterialPageRoute(
builder: builder,
fullscreenDialog: fullscreenDialog,
));
}
}
Future<bool> showConfirm(BuildContext context, String text) {
switch (theme) {
case ThemeMap.cupertino:
return showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text(text),
actions: <Widget>[
CupertinoDialogAction(
child: const Text('cancel'),
isDefaultAction: true,
onPressed: () {
Navigator.pop(context, false);
},
),
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
default:
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(
text,
// style: dialogTextStyle
),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context, false);
},
),
FlatButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, true);
},
)
],
);
},
);
}
}
Future<T> showDialogOptions<T>(
BuildContext context, List<DialogOption<T>> options) {
var title = Text('Pick your reaction');
var cancelWidget = Text('Cancel');
switch (theme) {
case ThemeMap.cupertino:
return showCupertinoDialog<T>(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: title,
actions: options.map((option) {
return CupertinoDialogAction(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(
CupertinoDialogAction(
child: cancelWidget,
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context, null);
},
),
),
);
},
);
default:
return showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: title,
children: options.map<Widget>((option) {
return SimpleDialogOption(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(SimpleDialogOption(
child: cancelWidget,
onPressed: () {
Navigator.pop(context, null);
},
)),
);
},
);
}
}
}

View File

@ -37,12 +37,6 @@ class PlatformType {
// Future<T> queryGitlab(SettingsProviderState settings);
// }
class ThemeMap {
static const material = 0;
static const cupertino = 1;
static const values = [0, 1];
}
class SettingsProvider extends StatefulWidget {
final Widget child;
@ -60,7 +54,6 @@ class SettingsProvider extends StatefulWidget {
class SettingsProviderState extends State<SettingsProvider> {
bool ready = false;
int theme;
Map<String, AccountModel> githubAccountMap;
Map<String, Map<String, Map<String, AccountModel>>> accountMap;
@ -76,16 +69,6 @@ class SettingsProviderState extends State<SettingsProvider> {
String prefix = 'https://api.github.com';
Future<void> setTheme(int _theme) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
theme = _theme;
await prefs.setInt(StorageKeys.theme, theme);
print('write theme: $theme');
setState(() {});
}
String get token {
if (activeLogin == null) return null;
@ -134,7 +117,7 @@ class SettingsProviderState extends State<SettingsProvider> {
'client_id': clientId,
'client_secret': clientSecret,
'code': code,
'state': randomString,
'state': _oauthState,
}),
);
// print(res.body);
@ -215,7 +198,7 @@ class SettingsProviderState extends State<SettingsProvider> {
}
void _initDataFromPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var prefs = await SharedPreferences.getInstance();
// read GitHub accounts
try {
@ -262,14 +245,6 @@ class SettingsProviderState extends State<SettingsProvider> {
accountMap = {};
}
int _theme = prefs.getInt(StorageKeys.theme);
print('read theme: $_theme');
if (ThemeMap.values.contains(_theme)) {
theme = _theme;
} else if (Platform.isIOS) {
theme = ThemeMap.cupertino;
}
setState(() {
ready = true;
});
@ -283,6 +258,9 @@ class SettingsProviderState extends State<SettingsProvider> {
});
}
Map<String, String> get _headers =>
{HttpHeaders.authorizationHeader: 'token $token'};
// http timeout
var _timeoutDuration = Duration(seconds: 10);
// var _timeoutDuration = Duration(seconds: 1);
@ -315,7 +293,7 @@ class SettingsProviderState extends State<SettingsProvider> {
}
Future<dynamic> getWithCredentials(String url, {String contentType}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
var headers = _headers;
if (contentType != null) {
// https://developer.github.com/v3/repos/contents/#custom-media-types
headers[HttpHeaders.contentTypeHeader] = contentType;
@ -328,69 +306,37 @@ class SettingsProviderState extends State<SettingsProvider> {
return data;
}
Future<dynamic> patchWithCredentials(String url) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
await http.patch(prefix + url, headers: headers).timeout(_timeoutDuration);
return true;
Future<void> patchWithCredentials(String url) async {
await http.patch(prefix + url, headers: _headers).timeout(_timeoutDuration);
}
Future<dynamic> putWithCredentials(String url,
Future<void> putWithCredentials(String url,
{String contentType, String body}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.put(prefix + url, headers: headers, body: body ?? {})
await http
.put(prefix + url, headers: _headers, body: body ?? {})
.timeout(_timeoutDuration);
// print(res.body);
return true;
}
Future<dynamic> postWithCredentials(String url,
Future<void> postWithCredentials(String url,
{String contentType, String body}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.post(prefix + url, headers: headers, body: body ?? {})
.timeout(_timeoutDuration);
// print(res.body);
return true;
}
Future<dynamic> deleteWithCredentials(String url) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.delete(prefix + url, headers: headers)
.post(prefix + url, headers: _headers, body: body ?? {})
.timeout(_timeoutDuration);
// print(res.body);
return true;
}
void pushRoute({
@required BuildContext context,
@required WidgetBuilder builder,
bool fullscreenDialog = false,
}) {
switch (theme) {
case ThemeMap.cupertino:
Navigator.of(context).push(CupertinoPageRoute(
builder: builder,
fullscreenDialog: fullscreenDialog,
));
break;
default:
Navigator.of(context).push(MaterialPageRoute(
builder: builder,
fullscreenDialog: fullscreenDialog,
));
}
Future<void> deleteWithCredentials(String url) async {
await http
.delete(prefix + url, headers: _headers)
.timeout(_timeoutDuration);
}
String randomString;
generateRandomString() {
randomString = nanoid();
return randomString;
String _oauthState;
void redirectToGithubOauth() {
_oauthState = nanoid();
launch(
'https://github.com/login/oauth/authorize?client_id=$clientId&redirect_uri=gittouch://login&scope=user%20repo&state=$_oauthState',
);
}
@override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../widgets/error_reload.dart';
import '../widgets/loading.dart';
import '../widgets/empty.dart';
@ -185,7 +186,7 @@ class _ListScaffoldState<T, K> extends State<ListScaffold<T, K>> {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
List<Widget> slivers = [
CupertinoSliverRefreshControl(onRefresh: _refresh)

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../widgets/loading.dart';
import '../widgets/link.dart';
import '../widgets/error_reload.dart';
@ -182,7 +183,7 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
List<Widget> slivers = [
CupertinoSliverRefreshControl(onRefresh: _refresh)

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../widgets/loading.dart';
import '../widgets/error_reload.dart';
@ -81,7 +82,7 @@ class _RefreshScaffoldState<T> extends State<RefreshScaffold<T>> {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../widgets/loading.dart';
import '../widgets/error_reload.dart';
@ -39,7 +40,7 @@ class RefreshStatelessScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoPageScaffold(
navigationBar:

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
class SimpleScaffold extends StatelessWidget {
final Widget title;
@ -20,7 +21,7 @@ class SimpleScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoPageScaffold(
navigationBar:

View File

@ -113,12 +113,7 @@ class _LoginScreenState extends State<LoginScreen> {
...accounts.map(_buildAccountItem),
_buildAddItem(
text: 'GitHub Account',
onTap: () {
var state = settings.generateRandomString();
launch(
'https://github.com/login/oauth/authorize?client_id=$clientId&redirect_uri=gittouch://login&scope=user%20repo&state=$state',
);
},
onTap: settings.redirectToGithubOauth,
),
// _buildAddItem(
// text: 'GitLab Account',

View File

@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import '../scaffolds/refresh_stateless.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/theme.dart';
import '../providers/settings.dart';
import '../widgets/notification_item.dart';
import '../widgets/list_group.dart';
@ -205,7 +206,7 @@ $key: pullRequest(number: ${item.number}) {
};
Widget _buildTitle() {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
// var textStyle = DefaultTextStyle.of(context).style;
return DefaultTextStyle(
@ -224,7 +225,8 @@ $key: pullRequest(number: ${item.number}) {
}
void _confirm() async {
var value = await showConfirm(context, 'Mark all as read?');
var value = await Provider.of<ThemeModel>(context)
.showConfirm(context, 'Mark all as read?');
if (value) {
await SettingsProvider.of(context).putWithCredentials('/notifications');
await _onSwitchTab();

View File

@ -2,8 +2,10 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/commits.dart';
import 'package:git_touch/screens/object.dart';
import 'package:provider/provider.dart';
import 'package:share/share.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/settings.dart';
@ -122,7 +124,7 @@ class _RepoScreenState extends State<RepoScreen> {
break;
}
SettingsProvider.of(context)
Provider.of<ThemeModel>(context)
.pushRoute(context: context, builder: builder);
},
),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import '../providers/settings.dart';
import '../scaffolds/simple.dart';
import '../utils/utils.dart';
@ -42,7 +44,7 @@ class _SearchScreenState extends State<SearchScreen> {
}
Widget _buildInput() {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoTextField(
// padding: EdgeInsets.all(10),
@ -60,14 +62,16 @@ class _SearchScreenState extends State<SearchScreen> {
return SimpleScaffold(
title: Text('Search GitHub Repositories'),
bodyBuilder: () {
return Column(children: <Widget>[
Container(padding: EdgeInsets.all(8), child: _buildInput()),
loading
? Loading()
: Column(
children: repos.map((repo) => RepoItem(repo)).toList(),
)
]);
return Column(
children: <Widget>[
Container(padding: EdgeInsets.all(8), child: _buildInput()),
loading
? Loading()
: Column(
children: repos.map((repo) => RepoItem(repo)).toList(),
)
],
);
},
);
}

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:launch_review/launch_review.dart';
import '../scaffolds/simple.dart';
import '../providers/settings.dart';
import '../widgets/table_view.dart';
import '../screens/repo.dart';
import '../screens/login.dart';
@ -15,7 +16,7 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> {
@override
Widget build(BuildContext context) {
var settings = SettingsProvider.of(context);
var themeProvider = Provider.of<ThemeModel>(context);
return SimpleScaffold(
title: Text('Settings'),
@ -36,19 +37,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
items: [
TableViewItem(
text: 'material',
checked: settings.theme == ThemeMap.material,
checked: themeProvider.theme == ThemeMap.material,
onTap: () {
if (settings.theme != ThemeMap.material) {
settings.setTheme(ThemeMap.material);
if (themeProvider.theme != ThemeMap.material) {
themeProvider.setTheme(ThemeMap.material);
}
},
),
TableViewItem(
text: 'cupertino',
checked: settings.theme == ThemeMap.cupertino,
checked: themeProvider.theme == ThemeMap.cupertino,
onTap: () {
if (settings.theme != ThemeMap.cupertino) {
settings.setTheme(ThemeMap.cupertino);
if (themeProvider.theme != ThemeMap.cupertino) {
themeProvider.setTheme(ThemeMap.cupertino);
}
},
),

View File

@ -34,125 +34,6 @@ void nextTick(Function callback, [int milliseconds = 0]) {
});
}
Future<bool> showConfirm(BuildContext context, String text) {
switch (SettingsProvider.of(context).theme) {
case ThemeMap.cupertino:
return showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text(text),
actions: <Widget>[
CupertinoDialogAction(
child: const Text('cancel'),
isDefaultAction: true,
onPressed: () {
Navigator.pop(context, false);
},
),
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
default:
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(
text,
// style: dialogTextStyle
),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context, false);
},
),
FlatButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, true);
},
)
],
);
},
);
}
}
class DialogOption<T> {
final T value;
final Widget widget;
DialogOption({this.value, this.widget});
}
Future<T> showDialogOptions<T>(
BuildContext context, List<DialogOption<T>> options) {
var title = Text('Pick your reaction');
var cancelWidget = Text('Cancel');
switch (SettingsProvider.of(context).theme) {
case ThemeMap.cupertino:
return showCupertinoDialog<T>(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: title,
actions: options.map((option) {
return CupertinoDialogAction(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(
CupertinoDialogAction(
child: cancelWidget,
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context, null);
},
),
),
);
},
);
default:
return showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: title,
children: options.map<Widget>((option) {
return SimpleDialogOption(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(SimpleDialogOption(
child: cancelWidget,
onPressed: () {
Navigator.pop(context, null);
},
)),
);
},
);
}
}
TextSpan createLinkSpan(BuildContext context, String text, Function handle) {
return TextSpan(
text: text,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../providers/settings.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/theme.dart';
class MyAction {
String text;
@ -28,7 +29,7 @@ class ActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return GestureDetector(
child: Icon(iconData, size: 24),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:primer/primer.dart';
import '../utils/utils.dart';
@ -102,19 +104,19 @@ class CommentItem extends StatelessWidget {
..add(
Link(
onTap: () async {
var result = await showDialogOptions(
context,
emojiMap.entries.map((entry) {
var emojiKey = entry.key;
return DialogOption(
value: emojiKey,
widget: Container(
decoration: _getDecorationByKey(emojiKey),
child: Text(emojiKey + ' ' + entry.value),
),
);
}).toList(),
);
var result = await Provider.of<ThemeModel>(context)
.showDialogOptions(
context,
emojiMap.entries.map((entry) {
var emojiKey = entry.key;
return DialogOption(
value: emojiKey,
widget: Container(
decoration: _getDecorationByKey(emojiKey),
child: Text(emojiKey + ' ' + entry.value),
),
);
}).toList());
onReaction(result, _hasReacted(result));
},
child: Container(

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
class Link extends StatelessWidget {
final Widget child;
@ -31,11 +32,10 @@ class Link extends StatelessWidget {
}
if (screenBuilder != null) {
SettingsProvider.of(context).pushRoute(
context: context,
builder: screenBuilder,
fullscreenDialog: fullscreenDialog,
);
Provider.of<ThemeModel>(context).pushRoute(
context: context,
builder: screenBuilder,
fullscreenDialog: fullscreenDialog);
}
if (url != null) {
@ -45,7 +45,7 @@ class Link extends StatelessWidget {
@override
Widget build(BuildContext context) {
var theme = SettingsProvider.of(context).theme;
var theme = Provider.of<ThemeModel>(context).theme;
if (iconButton != null) {
return IconButton(

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../providers/settings.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
class Loading extends StatelessWidget {
final bool more;
@ -10,7 +11,7 @@ class Loading extends StatelessWidget {
Widget _buildIndicator(BuildContext context) {
// return Image.asset('images/loading.webp');
switch (SettingsProvider.of(context).theme) {
switch (Provider.of<ThemeModel>(context).theme) {
case ThemeMap.cupertino:
return CupertinoActivityIndicator(radius: 12);
default: