bdaya_route
A mason brick to generate a page + controller that can react to route changes.
Generated by [mason][1] š§±
Note that we use
bdaya_flutter_common
as a base library for the generated code We also needbuild_runner
as a dev dependency.
Usage
- Activate mason_cli
dart pub global activate mason_cli
- Add Brick
mason add bdaya_route
mason get
- Make
mason make bdaya_route -o lib/src/pages --name UserDetails
Variables
name
: The route name
Generated code:
assuming input is UserDetails
view.dart
import 'package:bdaya_flutter_common/bdaya_flutter_common.dart';
import 'controller.dart';
import 'package:flutter/material.dart';
class UserDetailsView extends StatelessWidget {
const UserDetailsView({
super.key,
required this.controller,
});
static Widget hooked({
BdayaGetItHookMode hookMode = BdayaGetItHookMode.lazySingleton,
String? instanceName,
Object? param1,
Object? param2,
List<Object?>? keys,
}) => HookBuilder(
builder: (context) => UserDetailsView(
controller: useBdayaViewController(
hookMode: hookMode,
instanceName: instanceName,
keys: keys,
param1: param1,
param2: param2,
),
),
);
final UserDetailsController controller;
@override
Widget build(BuildContext context) {
final id = controller.idRx.of(context);
if (id == null) {
return const SizedBox.shrink();
}
//see also BdayaMultiLoadableAreaWrapper, and BdayaLoadableAreaWrapper.custom
return BdayaLoadableAreaWrapper(
area: controller.defaultArea,
builder: (context) {
//start using details after loading is done
final details = controller.detailsRx.of(context);
if (details == null) {
return const SizedBox.shrink();
}
//show details here
return const Placeholder();
},
// //show errors
// displayErrors: true,
// //customize error builder
// errorBuilder: (context, displayName, error, st) {
// },
// //customize loading builder
// isLoadingBuilder: (context, displayName) {
// },
);
}
}
controller.dart
import 'dart:async';
import 'package:bdaya_flutter_common/bdaya_flutter_common.dart';
import 'package:flutter/widgets.dart';
//TODO: DELETE ME!
class UserDetailsDto {
}
@lazySingleton
class UserDetailsController extends BdayaCombinedRouteController {
UserDetailsController(/*add getIt dependencies here*/) {
//show a loading indicator as soon as the page loads, optional.
defaultArea.startLoading();
}
//Add more route-dependant information here
final idRx = SharedValue<String?>(value: null);
final _refreshSignal = StreamController<DateTime>.broadcast();
//call this to refresh the details from the database
void refreshDetails() {
_refreshSignal.add(DateTime.now());
}
final detailsRx = SharedValue<UserDetailsDto?>(value: null);
final queryParamsRx = SharedValue<Map<String, String>>(value: {});
@override
bool get callOnRouteChangedInitially => true;
@override
void onRouteInformationChanged(GoRouterRouteMatch route) {
//this gets called for route changes to current (or child) routes
queryParamsRx.$ = route.uri.queryParameters;
//TODO: correct path parameters
idRx.$ = route.pathParameters['id'];
}
Future<UserDetailsDto?> initFromId(String id) async {
//TODO: fetch from database, api, etc...
//no need to try/catch here, since we are handling it in the stream below
return null;
}
@override
void beforeRender(BuildContext context) {
super.beforeRender(context);
// Register a stream to fetch data from the database based on changes to the id
registerStream(
Rx.combineLatest2(
_refreshSignal.stream.startWith(DateTime.now()),
idRx.streamWithInitial.distinct(),
(timestamp, idValue) => idValue, //We only care about the id
).switchMap((value) {
if (value == null) {
return Stream.value(null);
}
return initFromId(value).asStream().wrapWithArea(
defaultArea,
logger,
"An error occured while fetching data",
);
}).listen((event) {
detailsRx.$ = event;
}/*, onError: (error, stacktrace) {
//do some error logic, like showing a snackbar, just make sure context.mounted is true
//NOTE: it's recommended to not do anything here, and do error viewing logic in the view instead
}*/),
);
}
}