Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow barrier dismiss in dialogs #3

Merged
merged 5 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/fluorflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ when used with the fluorflow generator.
Dialogs work exactly the same way as bottom sheets, but are shown via the
`DialogService` and have another base class.

**Important:** Bottom sheets are always wrapped in a `Scaffold` widget. Thus,
they inherit your styles. `Dialogs` do not have this behavior (by design).
So you may create a full screen dialog and wrap it in a `Scaffold`, or
if you want a small "modal dialog" that has a backdrop and is dismissible
on click of the backdrop, it is also possible. However, you need
to wrap some parts of the content into a `Material` (or Theme provider)
widget to provide some decent default styles. Otherwise, some styles
are weird (e.g. Text Styles are big, red, and underlined).
You can see this in the examples (`SmallDialog`).

## CLI

FluorFlow comes with a CLI that can be used to generate views and other things.
Expand Down
35 changes: 35 additions & 0 deletions packages/fluorflow/example/lib/dialogs/small_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:fluorflow/annotations.dart';
import 'package:fluorflow/fluorflow.dart';
import 'package:flutter/material.dart';

@DialogConfig(
routeBuilder: RouteBuilder.topToBottomFade, defaultBarrierDismissible: true)
final class SmallDialog extends FluorFlowSimpleDialog<void> {
const SmallDialog({super.key, required super.completer});

@override
Widget build(BuildContext context) => Center(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Material(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
const Text('Dialog Page'),
const Text('Close via button or click into background'),
const SizedBox(height: 36),
ElevatedButton(
onPressed: completer.confirm,
child: const Text('Close'),
),
],
),
),
),
);
}
26 changes: 12 additions & 14 deletions packages/fluorflow/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:example/app.locator.dart';
import 'package:fluorflow/fluorflow.dart';
import 'package:flutter/material.dart';

import 'app.locator.dart';
import 'app.router.dart';

void main() async {
Expand All @@ -13,17 +13,15 @@ class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FluorFlow Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
initialRoute: AppRoute.homeView.path,
onGenerateRoute: onGenerateRoute,
navigatorKey: NavigationService.navigatorKey,
navigatorObservers: [NavigationService.observer()],
);
}
Widget build(BuildContext context) => MaterialApp(
title: 'FluorFlow Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
initialRoute: AppRoute.homeView.path,
onGenerateRoute: onGenerateRoute,
navigatorKey: NavigationService.navigatorKey,
navigatorObservers: [NavigationService.observer()],
);
}
4 changes: 4 additions & 0 deletions packages/fluorflow/example/lib/views/home/home_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ final class HomeView extends FluorFlowView<HomeViewModel> {
onPressed: viewModel.showTestDialog,
child: const Text('Show Dialog'),
),
ElevatedButton(
onPressed: viewModel.showSmallDialog,
child: const Text('Show Small Dialog'),
),
],
),
),
Expand Down
7 changes: 5 additions & 2 deletions packages/fluorflow/example/lib/views/home/home_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:example/app.dialogs.dart';
import 'package:example/app.router.dart';
import 'package:fluorflow/fluorflow.dart';

import '../../app.dialogs.dart';
import '../../app.router.dart';

final class HomeViewModel extends BaseViewModel {
final _dialogService = locator<DialogService>();
final _navService = locator<NavigationService>();
Expand All @@ -17,5 +18,7 @@ final class HomeViewModel extends BaseViewModel {

void showTestDialog() => _dialogService.showRedDialog(elements: []);

void showSmallDialog() => _dialogService.showSmallDialog();

void goToDetail() => _navService.navigateToDetailView();
}
5 changes: 5 additions & 0 deletions packages/fluorflow/lib/src/annotations/dialog_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ class DialogConfig {
/// The default barrier (background) color for the dialog.
final int defaultBarrierColor;

/// The default value for "barrierDismissible" for the dialog.
/// Defines whether the dialog can be dismissed by tapping the barrier.
final bool defaultBarrierDismissible;

/// The page route builder for the dialog.
final Type? pageRouteBuilder;

Expand All @@ -18,5 +22,6 @@ class DialogConfig {
this.pageRouteBuilder,
this.routeBuilder = RouteBuilder.noTransition,
this.defaultBarrierColor = 0x80000000,
this.defaultBarrierDismissible = false,
});
}
13 changes: 12 additions & 1 deletion packages/fluorflow/lib/src/dialogs/dialog_service.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import 'dart:math';

import 'package:flutter/widgets.dart';
import 'package:get/get.dart';

/// A service for showing and closing dialogs.
/// Works with route builders. However, it is recommended to use the
/// convenience methods generated by the generator to show a dialog.
class DialogService {
final _random = Random();

/// Returns whether a dialog is currently open.
bool get isDialogOpen => Get.isDialogOpen ?? false;

/// Shows a dialog and returns a future with the (possible) result.
/// The [barrierColor] parameter can be used to specify a custom barrier color for the dialog.
///
/// - The [barrierColor] parameter can be used to specify a custom barrier color for the dialog.
/// - The [barrierDismissible] parameter specifies whether the dialog can be dismissed by
/// tapping the barrier (if it is visible).
Future<TResult?> showDialog<TResult>({
required PageRouteBuilder dialogBuilder,
Color barrierColor = const Color(0x80000000),
bool barrierDismissible = false,
}) =>
Get.generalDialog<TResult>(
barrierDismissible: barrierDismissible,
barrierLabel:
barrierDismissible ? 'dialog_${_random.nextInt(1000000)}' : null,
pageBuilder: dialogBuilder.pageBuilder,
barrierColor: barrierColor,
transitionDuration: dialogBuilder.transitionDuration,
Expand Down
10 changes: 10 additions & 0 deletions packages/fluorflow_generator/lib/src/builder/dialog_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ class DialogBuilder implements Builder {
CodeExpression(Code(
'0x${(configAnnotation?.read('defaultBarrierColor').intValue ?? 0x80000000).toRadixString(16).padLeft(8, '0')}'))
]).code))
..optionalParameters.add(Parameter((b) => b
..name = 'barrierDismissible'
..type = refer('bool')
..named = true
..defaultTo = literalBool(configAnnotation
?.read('defaultBarrierDismissible')
.boolValue ??
false)
.code))
..optionalParameters.addAll(params.map((p) => Parameter((b) => b
..name = p.name
..type = recursiveTypeReference(lib, p.type)
Expand All @@ -120,6 +129,7 @@ class DialogBuilder implements Builder {
..body = refer('showDialog')
.call([], {
'barrierColor': refer('barrierColor'),
'barrierDismissible': refer('barrierDismissible'),
'dialogBuilder': dialogBuilder.newInstance([], {
'pageBuilder': Method((b) => b
..requiredParameters.add(Parameter((b) => b.name = '_'))
Expand Down
Loading