Skip to content

Commit

Permalink
State mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed Nov 17, 2023
1 parent 62ca70c commit a681740
Show file tree
Hide file tree
Showing 10 changed files with 593 additions and 261 deletions.
99 changes: 87 additions & 12 deletions lib/src/controller/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:octopus/src/controller/octopus.dart';
import 'package:octopus/src/state/state.dart';
import 'package:octopus/src/widget/octopus_navigator.dart';

/// Octopus delegate.
/// {@nodoc}
Expand Down Expand Up @@ -48,6 +49,10 @@ final class OctopusDelegate extends RouterDelegate<OctopusState>
@internal
set $controller(Octopus controller) => _controller = controller;

/// WidgetApp's navigator.
NavigatorState? get navigator => _modalObserver.navigator;
final NavigatorObserver _modalObserver = RouteObserver<ModalRoute<Object?>>();

T _handleErrors<T>(
T Function() callback, [
T Function(Object error, StackTrace stackTrace)? fallback,
Expand All @@ -66,26 +71,96 @@ final class OctopusDelegate extends RouterDelegate<OctopusState>
OctopusState get currentConfiguration =>
// ignore: prefer_expression_function_bodies
_handleErrors(() {
return value;
return value.copy();
});

@override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
Widget build(BuildContext context) => OctopusNavigator(
controller: _controller,
restorationScopeId: _restorationScopeId,
reportsRouteUpdateToEngine: true,
observers: <NavigatorObserver>[
_modalObserver,
...?_observers,
],
transitionDelegate: _transitionDelegate,
pages: _buildPages(context),
onPopPage: _onPopPage,
onUnknownRoute: _onUnknownRoute,
);

bool _onPopPage(Route<Object?> route, Object? result) => _handleErrors(
() {
if (!route.didPop(result)) return false;
final popped = value.maybePop();
if (popped == null) return false;
setNewRoutePath(popped);
return true;
},
(_, __) => false,
);

@override
Future<bool> popRoute() {
// TODO: implement popRoute
throw UnimplementedError();
}
Future<bool> popRoute() => _handleErrors(() {
final nav = navigator;
assert(nav != null, 'Navigator is not attached to the OctopusDelegate');
if (nav == null) return SynchronousFuture<bool>(false);
return nav.maybePop();
});

Route<Object?>? _onUnknownRoute(RouteSettings settings) => _handleErrors(
() {
final route = _notFound?.call(settings);
if (route != null) return route;
/* _onError?.call(
OctopusUnknownRouteException(settings),
StackTrace.current,
); */
return null;
},
(_, __) => null,
);

@override
Future<void> setNewRoutePath(OctopusState configuration) {
// TODO: implement setNewRoutePath
throw UnimplementedError();
Future<void> setNewRoutePath(covariant OctopusState configuration) {
_handleErrors(() {
// TODO(plugfox): make it async and show splash screen while loading
OctopusState? newConfiguration = configuration;
/* if (configuration is InvalidOctopusState) {
newConfiguration = _value;
_onError?.call(configuration.error, configuration.stackTrace);
} else {
final error = configuration.validate();
if (error != null) {
newConfiguration = _value;
_onError?.call(error, StackTrace.current);
}
} */

// TODO(plugfox): merge newConfiguration with currentConfiguration
// exclude dublicates and normolize

// If unchanged, do nothing
//if (_currentConfiguration == configuration) {
// return SynchronousFuture<void>(null);
//}

changeState(newConfiguration);
notifyListeners();
}, (_, __) {});

// Use [SynchronousFuture] so that the initial url is processed
// synchronously and remove unwanted initial animations on deep-linking
return SynchronousFuture<void>(null);
}

@override
Future<void> setInitialRoutePath(covariant OctopusState configuration) =>
setNewRoutePath(configuration);

@override
Future<void> setRestoredRoutePath(covariant OctopusState configuration) =>
setNewRoutePath(configuration);
}

mixin _OctopusStateObserver
Expand Down
32 changes: 32 additions & 0 deletions lib/src/controller/information_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:octopus/src/controller/state_codec.dart';
import 'package:octopus/src/state/state.dart';

/// Converts between [RouteInformation] and [OctopusState].
/// {@nodoc}
@internal
class OctopusInformationParser implements RouteInformationParser<OctopusState> {
/// {@nodoc}
OctopusInformationParser() : _codec = const OctopusStateCodec();

final OctopusStateCodec _codec;

@override
Future<OctopusState> parseRouteInformationWithDependencies(
RouteInformation routeInformation,
BuildContext context,
) =>
parseRouteInformation(routeInformation);

@override
Future<OctopusState> parseRouteInformation(RouteInformation route) =>
SynchronousFuture<OctopusState>(_codec.encode(route));

@override
RouteInformation? restoreRouteInformation(OctopusState state) =>
_codec.decode(state);
}
24 changes: 19 additions & 5 deletions lib/src/controller/information_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class OctopusInformationProvider extends RouteInformationProvider
}

return RouteInformation(
location: initialLocation ?? effectiveInitialLocation(),
uri: Uri.tryParse(initialLocation ?? effectiveInitialLocation()),
state: initialState,
);
}
Expand All @@ -58,13 +58,24 @@ class OctopusInformationProvider extends RouteInformationProvider
RouteInformation routeInformation, {
RouteInformationReportingType type = RouteInformationReportingType.none,
}) {
/* if (neglectIf != null && neglectIf!(routeInformation.uri.toString())) {
return;
} */

// Avoid adding a new history entry if the route is the same as before.
final replace = type == RouteInformationReportingType.neglect ||
(type == RouteInformationReportingType.none &&
_valueInEngine.location == routeInformation.location);
_valueInEngine.uri == routeInformation.uri &&
_valueInEngine.state == routeInformation.state);

/* if (!replace && routeInformation is OctopusRouteInformation) {
replace = routeInformation.replace;
} */

SystemNavigator.selectMultiEntryHistory();
SystemNavigator.routeInformationUpdated(
location: routeInformation.location,
uri: routeInformation.uri,
state: routeInformation.state,
replace: replace,
);
_value = routeInformation;
Expand All @@ -80,8 +91,11 @@ class OctopusInformationProvider extends RouteInformationProvider
if (shouldNotify) notifyListeners();
}

RouteInformation _valueInEngine =
RouteInformation(location: _binding.platformDispatcher.defaultRouteName);
RouteInformation _valueInEngine = RouteInformation(
uri: Uri.tryParse(
WidgetsBinding.instance.platformDispatcher.defaultRouteName,
),
);

void _platformReportsNewRouteInformation(RouteInformation routeInformation) {
if (_value == routeInformation) return;
Expand Down
33 changes: 19 additions & 14 deletions lib/src/controller/octopus.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:octopus/src/controller/delegate.dart';
import 'package:octopus/src/controller/information_parser.dart';
import 'package:octopus/src/controller/information_provider.dart';
import 'package:octopus/src/state/state.dart';

Expand All @@ -13,11 +14,10 @@ abstract base class Octopus {
factory Octopus({
required List<OctopusRoute> routes,
OctopusRoute? home,
/* String? restorationScopeId,
String? restorationScopeId,
List<NavigatorObserver>? observers,
TransitionDelegate<Object?>? transitionDelegate,
RouteFactory? notFound,
*/
void Function(Object error, StackTrace stackTrace)? onError,
}) = _OctopusImpl;

Expand Down Expand Up @@ -47,12 +47,13 @@ final class _OctopusImpl extends Octopus
factory _OctopusImpl({
required List<OctopusRoute> routes,
OctopusRoute? home,
/* String? restorationScopeId = 'octopus',
String? restorationScopeId = 'octopus',
List<NavigatorObserver>? observers,
TransitionDelegate<Object?>? transitionDelegate,
RouteFactory? notFound, */
RouteFactory? notFound,
void Function(Object error, StackTrace stackTrace)? onError,
}) {
assert(routes.isNotEmpty, 'Routes list should contain at least one route');
final list = List<OctopusRoute>.of(routes);
final defaultRoute = home ?? list.firstOrNull;
if (defaultRoute == null) {
Expand All @@ -62,14 +63,25 @@ final class _OctopusImpl extends Octopus
}
final routeInformationProvider = OctopusInformationProvider();
final backButtonDispatcher = RootBackButtonDispatcher();
throw UnimplementedError();
/* final controller = _OctopusImpl._(
final routeInformationParser = OctopusInformationParser();
final routerDelegate = OctopusDelegate(
initialState: OctopusState(
children: <OctopusNode>[defaultRoute.node()],
arguments: <String, String>{},
),
restorationScopeId: restorationScopeId,
observers: observers,
transitionDelegate: transitionDelegate,
notFound: notFound,
onError: onError,
);
final controller = _OctopusImpl._(
routeInformationProvider: routeInformationProvider,
routeInformationParser: routeInformationParser,
routerDelegate: routerDelegate,
backButtonDispatcher: backButtonDispatcher,
);
return controller; */
return controller;
}

_OctopusImpl._({
Expand Down Expand Up @@ -109,11 +121,4 @@ base mixin _OctopusNavigationMixin on _OctopusDelegateOwner, Octopus {
?.parseRouteInformation(RouteInformation(uri: location))
.then<void>(stateObserver.setNewRoutePath)
.ignore(); */

/// Push to the active navigation stack.
/// PushTo
/// Pop
/// PopFrom
/// Activate
/// Replace
}
40 changes: 40 additions & 0 deletions lib/src/controller/state_codec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dart:convert';

import 'package:flutter/widgets.dart';
import 'package:octopus/src/state/state.dart';

/// Converts [RouteInformation] to [OctopusState] and vice versa.
class OctopusStateCodec extends Codec<RouteInformation, OctopusState> {
/// Converts [RouteInformation] to [OctopusState] and vice versa.
const OctopusStateCodec();

@override
Converter<RouteInformation, OctopusState> get encoder =>
const OctopusStateEncoder();

@override
Converter<OctopusState, RouteInformation> get decoder =>
const OctopusStateDecoder();
}

/// Converts [RouteInformation] to [OctopusState].
class OctopusStateEncoder extends Converter<RouteInformation, OctopusState> {
/// Converts [RouteInformation] to [OctopusState].
const OctopusStateEncoder();

@override
OctopusState convert(RouteInformation input) =>
OctopusState.fromUri(input.uri);
}

/// Converts [OctopusState] to [RouteInformation].
class OctopusStateDecoder extends Converter<OctopusState, RouteInformation> {
/// Converts [OctopusState] to [RouteInformation].
const OctopusStateDecoder();

@override
RouteInformation convert(covariant OctopusState input) => RouteInformation(
uri: input.uri,
/* state: input, */
);
}
Loading

0 comments on commit a681740

Please sign in to comment.