feat(useragent): settled on routing architecture
This commit is contained in:
@@ -1,118 +0,0 @@
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
const transitionDuration = Duration(milliseconds: 800);
|
||||
|
||||
class AdaptiveBuilders {
|
||||
WidgetBuilder? buildSmall;
|
||||
WidgetBuilder? buildMediumLarge;
|
||||
WidgetBuilder? buildLarge;
|
||||
WidgetBuilder? buildExtraLarge;
|
||||
WidgetBuilder? build;
|
||||
|
||||
AdaptiveBuilders({
|
||||
this.buildSmall,
|
||||
this.buildMediumLarge,
|
||||
this.buildLarge,
|
||||
this.buildExtraLarge,
|
||||
this.build,
|
||||
});
|
||||
}
|
||||
|
||||
class Destination {
|
||||
final String label;
|
||||
final String? tooltip;
|
||||
final Icon icon;
|
||||
final Icon? selectedIcon;
|
||||
|
||||
final AdaptiveBuilders main;
|
||||
final AdaptiveBuilders? secondary;
|
||||
|
||||
Destination({
|
||||
required this.label,
|
||||
required this.icon,
|
||||
this.selectedIcon,
|
||||
required this.main,
|
||||
this.secondary,
|
||||
this.tooltip,
|
||||
});
|
||||
}
|
||||
|
||||
class Switcher extends StatelessWidget {
|
||||
final Widget? child;
|
||||
|
||||
const Switcher({super.key, this.child});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedSwitcher(
|
||||
duration: Duration(
|
||||
milliseconds: transitionDuration.inMilliseconds ~/ 100,
|
||||
),
|
||||
transitionBuilder: (child, animation) {
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WidgetBuilder? patchAnimated(WidgetBuilder? input) {
|
||||
if (input == null) return null;
|
||||
return (context) => Switcher(child: input(context));
|
||||
}
|
||||
|
||||
class HomeRouter extends HookWidget {
|
||||
final List<Destination> destinations;
|
||||
|
||||
HomeRouter({super.key, required this.destinations})
|
||||
: assert(destinations.isNotEmpty);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedIndex = useState(0);
|
||||
final destination = useMemoized(() => destinations[selectedIndex.value], [
|
||||
selectedIndex.value,
|
||||
]);
|
||||
final dispatcher = useMemoized(() => destination.main, [
|
||||
selectedIndex.value,
|
||||
]);
|
||||
final secondaryDispatcher = useMemoized(() => destination.secondary, [
|
||||
selectedIndex.value,
|
||||
]);
|
||||
|
||||
return AdaptiveScaffold(
|
||||
destinations: destinations
|
||||
.map(
|
||||
(destination) => NavigationDestination(
|
||||
label: destination.label,
|
||||
icon: destination.icon,
|
||||
selectedIcon: destination.selectedIcon,
|
||||
tooltip: destination.tooltip,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
selectedIndex: selectedIndex.value,
|
||||
onSelectedIndexChange: (index) => selectedIndex.value = index,
|
||||
useDrawer: true,
|
||||
|
||||
smallBody: patchAnimated(dispatcher.buildSmall),
|
||||
body: patchAnimated(dispatcher.build),
|
||||
mediumLargeBody: patchAnimated(dispatcher.buildMediumLarge),
|
||||
largeBody: patchAnimated(dispatcher.buildLarge),
|
||||
extraLargeBody: patchAnimated(dispatcher.buildExtraLarge),
|
||||
|
||||
smallSecondaryBody: patchAnimated(secondaryDispatcher?.buildSmall),
|
||||
secondaryBody: patchAnimated(secondaryDispatcher?.build),
|
||||
mediumLargeSecondaryBody: patchAnimated(
|
||||
secondaryDispatcher?.buildMediumLarge,
|
||||
),
|
||||
largeSecondaryBody: patchAnimated(secondaryDispatcher?.buildLarge),
|
||||
extraLargeSecondaryBody: patchAnimated(
|
||||
secondaryDispatcher?.buildExtraLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:arbiter/features/adaptive_switcher.dart';
|
||||
import 'package:arbiter/screens/about.dart';
|
||||
import 'package:arbiter/screens/calc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final _destinations = [
|
||||
Destination(
|
||||
label: "About",
|
||||
icon: Icon(Icons.info_outline),
|
||||
main: AdaptiveBuilders(build: (_) => About()),
|
||||
),
|
||||
Destination(
|
||||
label: "Calc",
|
||||
icon: Icon(Icons.calculate),
|
||||
main: AdaptiveBuilders(build: (_) => CalcScreen()),
|
||||
),
|
||||
];
|
||||
|
||||
class Home extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HomeRouter(destinations: _destinations);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,12 @@ import 'package:flutter/material.dart' hide Router;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(
|
||||
MaterialApp(
|
||||
home: ProviderScope(child: Scaffold(body: Router())),
|
||||
ProviderScope(
|
||||
child: MaterialApp.router(
|
||||
routerConfig: Router().config(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:hooks_riverpod/experimental/mutation.dart';
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:arbiter/features/pk_manager.dart';
|
||||
@@ -11,22 +10,22 @@ KeyManager keyManager(Ref ref) {
|
||||
return SimpleEd25519Manager();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@Riverpod(keepAlive: true)
|
||||
class Key extends _$Key {
|
||||
@override
|
||||
Future<KeyHandle?> build() async {
|
||||
final manager = SimpleEd25519Manager();
|
||||
final manager = ref.watch(keyManagerProvider);
|
||||
final keyHandle = await manager.get();
|
||||
return keyHandle;
|
||||
}
|
||||
|
||||
Future<void> create() async {
|
||||
final manager = ref.watch(keyManagerProvider);
|
||||
if (state.value != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = await AsyncValue.guard(() async {
|
||||
final manager = SimpleEd25519Manager();
|
||||
final newKeyHandle = await manager.create();
|
||||
return newKeyHandle;
|
||||
});
|
||||
@@ -34,7 +33,7 @@ class Key extends _$Key {
|
||||
}
|
||||
|
||||
class KeyBootstrapper implements StageFactory {
|
||||
final MutationTarget ref;
|
||||
final ProviderContainer ref;
|
||||
|
||||
KeyBootstrapper({required this.ref});
|
||||
|
||||
@@ -43,14 +42,14 @@ class KeyBootstrapper implements StageFactory {
|
||||
|
||||
@override
|
||||
Future<bool> get isAlreadyCompleted async {
|
||||
final key = await ref.container.read(keyProvider.future);
|
||||
final key = await ref.read(keyProvider.future);
|
||||
return key != null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> start(StageController controller) async {
|
||||
controller.setIndefiniteProgress();
|
||||
final key = ref.container.read(keyProvider.notifier);
|
||||
final key = ref.read(keyProvider.notifier);
|
||||
|
||||
await key.create();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ final class KeyProvider extends $AsyncNotifierProvider<Key, KeyHandle?> {
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'keyProvider',
|
||||
isAutoDispose: true,
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
@@ -73,7 +73,7 @@ final class KeyProvider extends $AsyncNotifierProvider<Key, KeyHandle?> {
|
||||
Key create() => Key();
|
||||
}
|
||||
|
||||
String _$keyHash() => r'6d66204174c4d2d5c76e27f3a8de8f9a9c88a3e0';
|
||||
String _$keyHash() => r'37b209825067adadbb75fe0b4ce936ea1c201dc8';
|
||||
|
||||
abstract class _$Key extends $AsyncNotifier<KeyHandle?> {
|
||||
FutureOr<KeyHandle?> build();
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
import 'dart:async';
|
||||
import 'package:arbiter/screens/dashboard/about.dart';
|
||||
import 'package:arbiter/screens/dashboard/calc.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
|
||||
import 'package:arbiter/features/bootstrap.dart';
|
||||
import 'package:arbiter/home.dart';
|
||||
import 'package:flutter/src/widgets/async.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'router.gr.dart';
|
||||
|
||||
final _bootstapCompleter = Completer<void>();
|
||||
|
||||
class Router extends HookConsumerWidget {
|
||||
@AutoRouterConfig(generateForDir: ['lib/screens'])
|
||||
class Router extends RootStackRouter {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bootstrapper = useMemoized(
|
||||
() => Bootstrap(completer: _bootstapCompleter),
|
||||
);
|
||||
final bootstrapFuture = useFuture(_bootstapCompleter.future);
|
||||
List<AutoRoute> get routes => [
|
||||
AutoRoute(page: Bootstrap.page, path: '/bootstrap', initial: true),
|
||||
|
||||
switch (bootstrapFuture.connectionState) {
|
||||
case ConnectionState.none ||
|
||||
ConnectionState.waiting ||
|
||||
ConnectionState.active:
|
||||
return bootstrapper;
|
||||
|
||||
case ConnectionState.done:
|
||||
break;
|
||||
}
|
||||
|
||||
return Home();
|
||||
}
|
||||
AutoRoute(
|
||||
page: DashboardRouter.page,
|
||||
path: '/dashboard',
|
||||
children: [
|
||||
AutoRoute(page: AboutRoute.page, path: 'about'),
|
||||
AutoRoute(page: CalcRoute.page, path: 'calc'),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
80
useragent/lib/router.gr.dart
Normal file
80
useragent/lib/router.gr.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
// dart format width=80
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// **************************************************************************
|
||||
// AutoRouterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// coverage:ignore-file
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
||||
import 'package:arbiter/screens/dashboard.dart' as _i4;
|
||||
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
||||
import 'package:arbiter/screens/dashboard/calc.dart' as _i3;
|
||||
import 'package:auto_route/auto_route.dart' as _i5;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.AboutScreen]
|
||||
class AboutRoute extends _i5.PageRouteInfo<void> {
|
||||
const AboutRoute({List<_i5.PageRouteInfo>? children})
|
||||
: super(AboutRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'AboutRoute';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return _i1.AboutScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.Bootstrap]
|
||||
class Bootstrap extends _i5.PageRouteInfo<void> {
|
||||
const Bootstrap({List<_i5.PageRouteInfo>? children})
|
||||
: super(Bootstrap.name, initialChildren: children);
|
||||
|
||||
static const String name = 'Bootstrap';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i2.Bootstrap();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i3.CalcScreen]
|
||||
class CalcRoute extends _i5.PageRouteInfo<void> {
|
||||
const CalcRoute({List<_i5.PageRouteInfo>? children})
|
||||
: super(CalcRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CalcRoute';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i3.CalcScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.DashboardRouter]
|
||||
class DashboardRouter extends _i5.PageRouteInfo<void> {
|
||||
const DashboardRouter({List<_i5.PageRouteInfo>? children})
|
||||
: super(DashboardRouter.name, initialChildren: children);
|
||||
|
||||
static const String name = 'DashboardRouter';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i4.DashboardRouter();
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
|
||||
class About extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AboutScreen(decription: "Arbiter is bla bla bla");
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,31 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:arbiter/providers/key.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:arbiter/router.gr.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:mtcore/markettakers.dart';
|
||||
|
||||
@RoutePage()
|
||||
class Bootstrap extends HookConsumerWidget {
|
||||
final Completer<void> completer;
|
||||
|
||||
const Bootstrap({required this.completer});
|
||||
const Bootstrap({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final container = ProviderScope.containerOf(context);
|
||||
final container = ProviderScope.containerOf( context);
|
||||
final completer = useMemoized(() {
|
||||
final completer = Completer<void>();
|
||||
completer.future.then((_) async {
|
||||
if (context.mounted) {
|
||||
final router = AutoRouter.of(context);
|
||||
router.replace(const DashboardRouter());
|
||||
}
|
||||
});
|
||||
|
||||
return completer;
|
||||
}, []);
|
||||
final stages = useMemoized(() {
|
||||
return [KeyBootstrapper(ref: container)];
|
||||
}, []);
|
||||
@@ -21,6 +33,7 @@ class Bootstrap extends HookConsumerWidget {
|
||||
() => Bootstrapper(stages: stages, onCompleted: completer),
|
||||
[stages],
|
||||
);
|
||||
|
||||
return bootstrapper;
|
||||
}
|
||||
}
|
||||
42
useragent/lib/screens/dashboard.dart
Normal file
42
useragent/lib/screens/dashboard.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:arbiter/router.gr.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
|
||||
const breakpoints = MaterialAdaptiveBreakpoints();
|
||||
|
||||
final routes = [AboutRoute(), CalcRoute()];
|
||||
|
||||
@RoutePage()
|
||||
class DashboardRouter extends StatelessWidget {
|
||||
const DashboardRouter({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AutoTabsRouter(
|
||||
routes: routes,
|
||||
transitionBuilder: (context, child, animation) => FadeTransition(
|
||||
opacity: animation,
|
||||
// the passed child is technically our animated selected-tab page
|
||||
child: child,
|
||||
),
|
||||
builder: (context, child) {
|
||||
final tabsRouter = AutoTabsRouter.of(context);
|
||||
final currentActive = tabsRouter.activeIndex;
|
||||
return AdaptiveScaffold(
|
||||
destinations: [
|
||||
NavigationDestination(icon: Icon(Icons.book), label: "About"),
|
||||
NavigationDestination(icon: Icon(Icons.calculate), label: "Calc"),
|
||||
],
|
||||
body: (ctx) => child,
|
||||
onSelectedIndexChange: (index) {
|
||||
tabsRouter.navigate(routes[index]);
|
||||
},
|
||||
selectedIndex: currentActive,
|
||||
transitionDuration: Duration(milliseconds: 800),
|
||||
internalAnimations: true,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
12
useragent/lib/screens/dashboard/about.dart
Normal file
12
useragent/lib/screens/dashboard/about.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mtcore/markettakers.dart' as mt;
|
||||
|
||||
|
||||
@RoutePage()
|
||||
class AboutScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return mt.AboutScreen(decription: "Arbiter is bla bla bla");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CalcScreen extends StatefulWidget {
|
||||
const CalcScreen({super.key});
|
||||
|
||||
Reference in New Issue
Block a user