Add LinearProgressIndicator for background queries
This commit is contained in:
parent
beb1005710
commit
8126327b95
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/layout/app_bar_progress_indicator.dart';
|
||||||
import 'package:simplecloudnotifier/pages/debug/debug_main.dart';
|
import 'package:simplecloudnotifier/pages/debug/debug_main.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_theme.dart';
|
import 'package:simplecloudnotifier/state/app_theme.dart';
|
||||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
@ -71,6 +72,10 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
title: Text(title ?? 'Simple Cloud Notifier 2.0'),
|
title: Text(title ?? 'Simple Cloud Notifier 2.0'),
|
||||||
actions: actions,
|
actions: actions,
|
||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size(double.infinity, 1.0),
|
||||||
|
child: AppBarProgressIndicator(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
|
|
||||||
|
class AppBarProgressIndicator extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
@override
|
||||||
|
Size get preferredSize => Size(double.infinity, 1.0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<AppBarState>(
|
||||||
|
builder: (context, value, child) {
|
||||||
|
if (value.loadingIndeterminate) {
|
||||||
|
return LinearProgressIndicator(value: null);
|
||||||
|
} else {
|
||||||
|
return SizedBox.square(dimension: 4); // 4 height is the same as the LinearProgressIndicator
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,14 @@ import 'package:hive_flutter/hive_flutter.dart';
|
|||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
import 'package:simplecloudnotifier/models/client.dart';
|
import 'package:simplecloudnotifier/models/client.dart';
|
||||||
import 'package:simplecloudnotifier/nav_layout.dart';
|
import 'package:simplecloudnotifier/nav_layout.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_theme.dart';
|
import 'package:simplecloudnotifier/state/app_theme.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/globals.dart';
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
import 'package:simplecloudnotifier/state/request_log.dart';
|
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
import 'package:toastification/toastification.dart';
|
import 'package:toastification/toastification.dart';
|
||||||
import 'firebase_options.dart';
|
import 'firebase_options.dart';
|
||||||
|
|
||||||
@ -113,8 +115,9 @@ void main() async {
|
|||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (context) => AppAuth(), lazy: false),
|
ChangeNotifierProvider(create: (context) => AppAuth(), lazy: false),
|
||||||
ChangeNotifierProvider(create: (context) => AppTheme(), lazy: false),
|
ChangeNotifierProvider(create: (context) => AppTheme(), lazy: false),
|
||||||
|
ChangeNotifierProvider(create: (context) => AppBarState(), lazy: false),
|
||||||
],
|
],
|
||||||
child: const SCNApp(),
|
child: SCNApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -156,7 +159,7 @@ void setFirebaseToken(String fcmToken) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SCNApp extends StatelessWidget {
|
class SCNApp extends StatelessWidget {
|
||||||
const SCNApp({super.key});
|
SCNApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -169,6 +172,7 @@ class SCNApp extends StatelessWidget {
|
|||||||
child: Consumer<AppTheme>(
|
child: Consumer<AppTheme>(
|
||||||
builder: (context, appTheme, child) => MaterialApp(
|
builder: (context, appTheme, child) => MaterialApp(
|
||||||
title: 'SimpleCloudNotifier',
|
title: 'SimpleCloudNotifier',
|
||||||
|
navigatorObservers: [Navi.routeObserver],
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
//TODO color settings
|
//TODO color settings
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: appTheme.darkMode ? Brightness.dark : Brightness.light),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: appTheme.darkMode ? Brightness.dark : Brightness.light),
|
||||||
|
@ -11,6 +11,7 @@ import 'package:simplecloudnotifier/models/keytoken.dart';
|
|||||||
import 'package:simplecloudnotifier/models/message.dart';
|
import 'package:simplecloudnotifier/models/message.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
|
||||||
@ -32,32 +33,38 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
|
||||||
mainFuture = fetchData();
|
mainFuture = fetchData();
|
||||||
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<(Message, ChannelPreview, KeyTokenPreview, UserPreview)> fetchData() async {
|
Future<(Message, ChannelPreview, KeyTokenPreview, UserPreview)> fetchData() async {
|
||||||
final acc = Provider.of<AppAuth>(context, listen: false);
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 0), () {}); // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
|
||||||
|
|
||||||
final msg = await APIClient.getMessage(acc, widget.message.messageID);
|
AppBarState().setLoadingIndeterminate(true);
|
||||||
|
|
||||||
final fut_chn = APIClient.getChannelPreview(acc, msg.channelID);
|
final acc = Provider.of<AppAuth>(context, listen: false);
|
||||||
final fut_key = APIClient.getKeyTokenPreview(acc, msg.usedKeyID);
|
|
||||||
final fut_usr = APIClient.getUserPreview(acc, msg.senderUserID);
|
|
||||||
|
|
||||||
final chn = await fut_chn;
|
final msg = await APIClient.getMessage(acc, widget.message.messageID);
|
||||||
final key = await fut_key;
|
|
||||||
final usr = await fut_usr;
|
|
||||||
|
|
||||||
//TODO getShortUser for sender
|
final fut_chn = APIClient.getChannelPreview(acc, msg.channelID);
|
||||||
|
final fut_key = APIClient.getKeyTokenPreview(acc, msg.usedKeyID);
|
||||||
|
final fut_usr = APIClient.getUserPreview(acc, msg.senderUserID);
|
||||||
|
|
||||||
//await Future.delayed(const Duration(seconds: 2), () {});
|
final chn = await fut_chn;
|
||||||
|
final key = await fut_key;
|
||||||
|
final usr = await fut_usr;
|
||||||
|
|
||||||
final r = (msg, chn, key, usr);
|
//await Future.delayed(const Duration(seconds: 10), () {});
|
||||||
|
|
||||||
mainFutureSnapshot = r;
|
final r = (msg, chn, key, usr);
|
||||||
|
|
||||||
return r;
|
mainFutureSnapshot = r;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
} finally {
|
||||||
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -77,11 +84,11 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final (msg, chn, tok, usr) = snapshot.data!;
|
final (msg, chn, tok, usr) = snapshot.data!;
|
||||||
return _buildMessageView(context, msg, chn, tok, usr, false);
|
return _buildMessageView(context, msg, chn, tok, usr);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
return Center(child: Text('${snapshot.error}')); //TODO nice error page
|
return Center(child: Text('${snapshot.error}')); //TODO nice error page
|
||||||
} else if (!widget.message.trimmed) {
|
} else if (!widget.message.trimmed) {
|
||||||
return _buildMessageView(context, widget.message, null, null, null, true);
|
return _buildMessageView(context, widget.message, null, null, null);
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
@ -111,7 +118,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageView(BuildContext context, Message message, ChannelPreview? channel, KeyTokenPreview? token, UserPreview? user, bool loading) {
|
Widget _buildMessageView(BuildContext context, Message message, ChannelPreview? channel, KeyTokenPreview? token, UserPreview? user) {
|
||||||
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
@ -120,7 +127,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
..._buildMessageHeader(context, message, channel, loading),
|
..._buildMessageHeader(context, message, channel),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
if (message.content != null) ..._buildMessageContent(context, message),
|
if (message.content != null) ..._buildMessageContent(context, message),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
@ -141,7 +148,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
return channel?.displayName ?? message.channelInternalName;
|
return channel?.displayName ?? message.channelInternalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildMessageHeader(BuildContext context, Message message, ChannelPreview? channel, bool loading) {
|
List<Widget> _buildMessageHeader(BuildContext context, Message message, ChannelPreview? channel) {
|
||||||
return [
|
return [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -156,24 +163,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
if (!loading) Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
if (loading)
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Flexible(child: Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold))),
|
|
||||||
SizedBox(height: 20, width: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: SizedBox(width: 0)),
|
|
||||||
SizedBox(child: CircularProgressIndicator(), height: 20, width: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
flutter/lib/state/app_bar_state.dart
Normal file
20
flutter/lib/state/app_bar_state.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class AppBarState extends ChangeNotifier {
|
||||||
|
static AppBarState? _singleton = AppBarState._internal();
|
||||||
|
|
||||||
|
factory AppBarState() {
|
||||||
|
return _singleton ?? (_singleton = AppBarState._internal());
|
||||||
|
}
|
||||||
|
|
||||||
|
AppBarState._internal() {}
|
||||||
|
|
||||||
|
bool _loadingIndeterminate = false;
|
||||||
|
bool get loadingIndeterminate => _loadingIndeterminate;
|
||||||
|
|
||||||
|
void setLoadingIndeterminate(bool v) {
|
||||||
|
if (_loadingIndeterminate == v) return;
|
||||||
|
_loadingIndeterminate = v;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,51 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
|
|
||||||
class Navi {
|
class Navi {
|
||||||
|
static final SCNRouteObserver routeObserver = SCNRouteObserver();
|
||||||
|
|
||||||
static void push<T extends Widget>(BuildContext context, T Function() builder) {
|
static void push<T extends Widget>(BuildContext context, T Function() builder) {
|
||||||
|
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute<T>(builder: (context) => builder()));
|
Navigator.push(context, MaterialPageRoute<T>(builder: (context) => builder()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void popToRoot(BuildContext context) {
|
static void popToRoot(BuildContext context) {
|
||||||
|
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
||||||
|
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
||||||
|
@override
|
||||||
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
|
super.didPush(route, previousRoute);
|
||||||
|
if (route is PageRoute) {
|
||||||
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
|
||||||
|
print('[SCNRouteObserver] .didPush()');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
|
||||||
|
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
|
||||||
|
if (newRoute is PageRoute) {
|
||||||
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
|
||||||
|
print('[SCNRouteObserver] .didReplace()');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
|
super.didPop(route, previousRoute);
|
||||||
|
if (previousRoute is PageRoute && route is PageRoute) {
|
||||||
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
|
||||||
|
print('[SCNRouteObserver] .didPop()');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user