feat: bottom sheets with transition (#4608)

* feat: shiny new bottom sheets

* chore: code review
This commit is contained in:
Richard Shiue 2024-02-22 09:54:54 +08:00 committed by GitHub
parent 7802c75d7c
commit 4a0c48f419
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 441 additions and 98 deletions

View File

@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdlib.h>
int64_t init_sdk(char *data);
int64_t init_sdk(int64_t port, char *data);
void async_event(int64_t port, const uint8_t *input, uintptr_t len);

View File

@ -7,3 +7,4 @@ export 'bottom_sheet_view_item_body.dart';
export 'bottom_sheet_view_page.dart';
export 'default_mobile_action_pane.dart';
export 'show_mobile_bottom_sheet.dart';
export 'show_transition_bottom_sheet.dart';

View File

@ -79,7 +79,7 @@ Future<T?> showMobileBottomSheet<T>(
if (showHeader) {
children.add(
_Header(
BottomSheetHeader(
showCloseButton: showCloseButton,
showBackButton: showBackButton,
showDoneButton: showDoneButton,
@ -152,8 +152,9 @@ Future<T?> showMobileBottomSheet<T>(
);
}
class _Header extends StatelessWidget {
const _Header({
class BottomSheetHeader extends StatelessWidget {
const BottomSheetHeader({
super.key,
required this.showBackButton,
required this.showCloseButton,
required this.title,

View File

@ -0,0 +1,345 @@
import 'dart:math' as math;
import 'package:appflowy/plugins/base/drag_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
import 'show_mobile_bottom_sheet.dart';
Future<T?> showTransitionMobileBottomSheet<T>(
BuildContext context, {
required WidgetBuilder builder,
bool useRootNavigator = false,
EdgeInsets contentPadding = EdgeInsets.zero,
Color? backgroundColor,
// drag handle
bool showDragHandle = false,
// header
bool showHeader = false,
String title = '',
bool showBackButton = false,
bool showCloseButton = false,
bool showDoneButton = false,
bool showDivider = true,
// stops
double initialStop = 1.0,
List<double>? stops,
}) {
assert(
showHeader ||
title.isEmpty &&
!showCloseButton &&
!showBackButton &&
!showDoneButton &&
!showDivider,
);
assert(!(showCloseButton && showBackButton));
backgroundColor ??= Theme.of(context).brightness == Brightness.light
? const Color(0xFFF7F8FB)
: const Color(0xFF626364);
return Navigator.of(
context,
rootNavigator: useRootNavigator,
).push<T>(
TransitionSheetRoute<T>(
backgroundColor: backgroundColor,
initialStop: initialStop,
stops: stops,
builder: (context) {
final Widget child = builder(context);
// if the children is only one, we don't need to wrap it with a column
if (!showDragHandle && !showHeader) {
return child;
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (showDragHandle) const DragHandle(),
if (showHeader) ...[
BottomSheetHeader(
showCloseButton: showCloseButton,
showBackButton: showBackButton,
showDoneButton: showDoneButton,
title: title,
),
if (showDivider)
const Divider(
height: 0.5,
thickness: 0.5,
),
],
Expanded(
child: Padding(
padding: contentPadding,
child: child,
),
),
],
);
},
),
);
}
/// The top offset that will be displayed from the bottom route
const double _kPreviousRouteVisibleOffset = 10.0;
/// Minimal distance from the top of the screen to the top of the previous route
/// It will be used ff the top safe area is less than this value.
/// In iPhones the top SafeArea is more or equal to this distance.
const double _kSheetMinimalOffset = 10;
const Curve _kCupertinoSheetCurve = Curves.easeOutExpo;
const Curve _kCupertinoTransitionCurve = Curves.linear;
/// Wraps the child into a cupertino modal sheet appearance. This is used to
/// create a [SheetRoute].
///
/// Clip the child widget to rectangle with top rounded corners and adds
/// top padding and top safe area.
class _CupertinoSheetDecorationBuilder extends StatelessWidget {
const _CupertinoSheetDecorationBuilder({
required this.child,
required this.topRadius,
this.backgroundColor,
});
/// The child contained by the modal sheet
final Widget child;
/// The color to paint behind the child
final Color? backgroundColor;
/// The top corners of this modal sheet are rounded by this Radius
final Radius topRadius;
@override
Widget build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
return Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(top: topRadius),
color: backgroundColor,
),
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: child,
),
);
},
);
}
}
/// Customized CupertinoSheetRoute from the sheets package
///
/// A modal route that overlays a widget over the current route and animates
/// it from the bottom with a cupertino modal sheet appearance
///
/// Clip the child widget to rectangle with top rounded corners and adds
/// top padding and top safe area.
class TransitionSheetRoute<T> extends SheetRoute<T> {
TransitionSheetRoute({
required WidgetBuilder builder,
super.stops,
double initialStop = 1.0,
super.settings,
Color? backgroundColor,
super.maintainState = true,
super.fit,
}) : super(
builder: (BuildContext context) {
return _CupertinoSheetDecorationBuilder(
backgroundColor: backgroundColor,
topRadius: const Radius.circular(16),
child: Builder(builder: builder),
);
},
animationCurve: _kCupertinoSheetCurve,
initialExtent: initialStop,
);
@override
bool get draggable => true;
final SheetController _sheetController = SheetController();
@override
SheetController createSheetController() => _sheetController;
@override
Color? get barrierColor => Colors.transparent;
@override
bool get barrierDismissible => true;
@override
Widget buildSheet(BuildContext context, Widget child) {
final effectivePhysics = draggable
? BouncingSheetPhysics(
parent: SnapSheetPhysics(
stops: stops ?? <double>[0, 1],
parent: physics,
),
)
: const NeverDraggableSheetPhysics();
final MediaQueryData mediaQuery = MediaQuery.of(context);
final double topMargin =
math.max(_kSheetMinimalOffset, mediaQuery.padding.top) +
_kPreviousRouteVisibleOffset;
return Sheet.raw(
initialExtent: initialExtent,
decorationBuilder: decorationBuilder,
fit: fit,
maxExtent: mediaQuery.size.height - topMargin,
physics: effectivePhysics,
controller: sheetController,
child: child,
);
}
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
final double topPadding = MediaQuery.of(context).padding.top;
final double topOffset = math.max(_kSheetMinimalOffset, topPadding);
return AnimatedBuilder(
animation: secondaryAnimation,
child: child,
builder: (BuildContext context, Widget? child) {
final double progress = secondaryAnimation.value;
final double scale = 1 - progress / 10;
final double distanceWithScale =
(topOffset + _kPreviousRouteVisibleOffset) * 0.9;
final Offset offset =
Offset(0, progress * (topOffset - distanceWithScale));
return Transform.translate(
offset: offset,
child: Transform.scale(
scale: scale,
alignment: Alignment.topCenter,
child: child,
),
);
},
);
}
@override
bool canDriveSecondaryTransitionForPreviousRoute(
Route<dynamic> previousRoute,
) =>
true;
@override
Widget buildSecondaryTransitionForPreviousRoute(
BuildContext context,
Animation<double> secondaryAnimation,
Widget child,
) {
final Animation<double> delayAnimation = CurvedAnimation(
parent: _sheetController.animation,
curve: Interval(
initialExtent == 1 ? 0 : initialExtent,
1,
),
);
final Animation<double> secondaryAnimation = CurvedAnimation(
parent: _sheetController.animation,
curve: Interval(
0,
initialExtent,
),
);
return CupertinoSheetBottomRouteTransition(
body: child,
sheetAnimation: delayAnimation,
secondaryAnimation: secondaryAnimation,
);
}
}
/// Animation for previous route when a [TransitionSheetRoute] enters/exits
@visibleForTesting
class CupertinoSheetBottomRouteTransition extends StatelessWidget {
const CupertinoSheetBottomRouteTransition({
super.key,
required this.sheetAnimation,
required this.secondaryAnimation,
required this.body,
});
final Widget body;
final Animation<double> sheetAnimation;
final Animation<double> secondaryAnimation;
@override
Widget build(BuildContext context) {
final double topPadding = MediaQuery.of(context).padding.top;
final double topOffset = math.max(_kSheetMinimalOffset, topPadding);
final CurvedAnimation curvedAnimation = CurvedAnimation(
parent: sheetAnimation,
curve: _kCupertinoTransitionCurve,
);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: AnimatedBuilder(
animation: secondaryAnimation,
child: body,
builder: (BuildContext context, Widget? child) {
final double progress = curvedAnimation.value;
final double scale = 1 - progress / 10;
return Stack(
children: <Widget>[
Container(color: Colors.black),
Transform.translate(
offset: Offset(0, progress * topOffset),
child: Transform.scale(
scale: scale,
alignment: Alignment.topCenter,
child: ClipRRect(
borderRadius: BorderRadius.vertical(
top: Radius.lerp(
Radius.zero,
const Radius.circular(16.0),
progress,
)!,
),
child: ColorFiltered(
colorFilter: ColorFilter.mode(
(Theme.of(context).brightness == Brightness.dark
? Colors.grey
: Colors.black)
.withOpacity(secondaryAnimation.value * 0.1),
BlendMode.srcOver,
),
child: child,
),
),
),
),
],
);
},
),
);
}
}

View File

@ -44,7 +44,6 @@ void showCreateFieldBottomSheet(
elevation: 20,
title: LocaleKeys.grid_field_newProperty.tr(),
backgroundColor: Theme.of(context).colorScheme.surface,
barrierColor: Colors.transparent,
enableDraggableScrollable: true,
builder: (context) {
final typeOptionMenuItemValue = mobileSupportedFieldTypes

View File

@ -447,14 +447,12 @@ class _PropertyType extends StatelessWidget {
onTap: () {
showMobileBottomSheet(
context,
padding: EdgeInsets.zero,
showHeader: true,
showDragHandle: true,
showCloseButton: true,
elevation: 20,
title: LocaleKeys.grid_field_editProperty.tr(),
backgroundColor: Theme.of(context).colorScheme.surface,
barrierColor: Colors.transparent,
enableDraggableScrollable: true,
builder: (context) {
final typeOptionMenuItemValue = mobileSupportedFieldTypes

View File

@ -68,7 +68,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
viewId: viewId,
fieldController: databaseController.fieldController,
fieldInfo: firstField,
showTopBorder: true,
showTopBorder: false,
);
final cells = fields
.mapIndexed(
@ -124,9 +124,10 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
children: [
_divider(),
_NewDatabaseFieldTile(viewId: viewId),
const VSpace(24),
],
)
: null,
: const VSpace(24),
itemCount: cells.length,
itemBuilder: (context, index) => cells[index],
);

View File

@ -3,7 +3,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/plugins/base/drag_handler.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
@ -27,20 +26,12 @@ class MobileDatabaseViewList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DraggableScrollableSheet(
expand: false,
snap: true,
initialChildSize: 0.98,
minChildSize: 0.98,
maxChildSize: 0.98,
builder: (context, scrollController) {
return BlocBuilder<ViewBloc, ViewState>(
builder: (context, state) {
final views = [state.view, ...state.view.childViews];
return Column(
children: [
const DragHandle(),
_Header(
title: LocaleKeys.grid_settings_viewList.plural(
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
@ -54,7 +45,6 @@ class MobileDatabaseViewList extends StatelessWidget {
onDone: (context) => Navigator.pop(context),
),
SingleChildScrollView(
controller: scrollController,
child: Column(
children: [
...views.mapIndexed(
@ -72,8 +62,6 @@ class MobileDatabaseViewList extends StatelessWidget {
);
},
);
},
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_transition_bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
@ -33,15 +33,11 @@ class MobileDatabaseViewQuickActions extends StatelessWidget {
_actionButton(context, _Action.edit, () {
final bloc = context.read<ViewBloc>();
context.pop();
showMobileBottomSheet(
showTransitionMobileBottomSheet(
context,
showHeader: true,
showDoneButton: true,
title: LocaleKeys.grid_settings_editView.tr(),
enableDraggableScrollable: true,
initialChildSize: 0.98,
minChildSize: 0.98,
maxChildSize: 0.98,
builder: (_) => BlocProvider.value(
value: bloc,
child: MobileEditDatabaseViewScreen(

View File

@ -33,8 +33,7 @@ class MobileEditDatabaseViewScreen extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<ViewBloc, ViewState>(
builder: (context, state) {
return ListView(
shrinkWrap: true,
return Column(
children: [
_NameAndIcon(view: state.view),
_divider(),
@ -91,13 +90,15 @@ class _NameAndIconState extends State<_NameAndIcon> {
@override
Widget build(BuildContext context) {
return FlowyOptionTile.textField(
return Material(
child: FlowyOptionTile.textField(
autofocus: true,
showTopBorder: false,
controller: textEditingController,
onTextChanged: (text) {
context.read<ViewBloc>().add(ViewEvent.rename(text));
},
),
);
}
}
@ -228,18 +229,12 @@ class DatabaseViewSettingTile extends StatelessWidget {
}
if (setting == DatabaseViewSettings.fields) {
await showMobileBottomSheet(
await showTransitionMobileBottomSheet(
context,
useSafeArea: false,
showDragHandle: true,
showHeader: true,
showBackButton: true,
title: LocaleKeys.grid_settings_properties.tr(),
showDivider: true,
enableDraggableScrollable: true,
initialChildSize: 0.98,
minChildSize: 0.98,
maxChildSize: 0.98,
builder: (_) {
return BlocProvider.value(
value: context.read<ViewBloc>(),

View File

@ -1,5 +1,5 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_transition_bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/database/view/database_view_list.dart';
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
@ -108,9 +108,10 @@ class _DatabaseViewSelectorButton extends StatelessWidget {
],
),
onPressed: () {
showMobileBottomSheet(
showTransitionMobileBottomSheet(
context,
showDivider: false,
initialStop: 1.0,
builder: (_) {
return MultiBlocProvider(
providers: [

View File

@ -58,9 +58,8 @@ class MobileDatabaseControls extends StatelessWidget {
return _DatabaseControlButton(
icon: FlowySvgs.m_field_hide_s,
onTap: () => showMobileBottomSheet(
onTap: () => showTransitionMobileBottomSheet(
context,
showDragHandle: true,
showHeader: true,
showBackButton: true,
title: LocaleKeys.grid_settings_properties.tr(),

View File

@ -30,6 +30,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra/time/duration.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:sheet/route.dart';
GoRouter generateRouter(Widget child) {
return GoRouter(
@ -193,7 +194,7 @@ GoRoute _mobileHomeSettingPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileHomeSettingPage.routeName,
pageBuilder: (context, state) {
return const MaterialPage(child: MobileHomeSettingPage());
return const MaterialExtendedPage(child: MobileHomeSettingPage());
},
);
}
@ -203,7 +204,7 @@ GoRoute _mobileCloudSettingAppFlowyCloudPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: AppFlowyCloudPage.routeName,
pageBuilder: (context, state) {
return const MaterialPage(child: AppFlowyCloudPage());
return const MaterialExtendedPage(child: AppFlowyCloudPage());
},
);
}
@ -213,7 +214,7 @@ GoRoute _mobileLaunchSettingsPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileLaunchSettingsPage.routeName,
pageBuilder: (context, state) {
return const MaterialPage(child: MobileLaunchSettingsPage());
return const MaterialExtendedPage(child: MobileLaunchSettingsPage());
},
);
}
@ -223,7 +224,7 @@ GoRoute _mobileHomeTrashPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileHomeTrashPage.routeName,
pageBuilder: (context, state) {
return const MaterialPage(child: MobileHomeTrashPage());
return const MaterialExtendedPage(child: MobileHomeTrashPage());
},
);
}
@ -239,7 +240,7 @@ GoRoute _mobileBlockSettingsPageRoute() {
?.split(',')
.map(MobileBlockActionType.fromActionString)
.toList();
return MaterialPage(
return MaterialExtendedPage(
child: MobileBlockSettingsScreen(
actions: actions ?? MobileBlockActionType.standard,
),
@ -255,7 +256,7 @@ GoRoute _mobileEmojiPickerPageRoute() {
pageBuilder: (context, state) {
final title =
state.uri.queryParameters[MobileEmojiPickerScreen.pageTitle];
return MaterialPage(
return MaterialExtendedPage(
child: MobileEmojiPickerScreen(
title: title,
),
@ -271,7 +272,7 @@ GoRoute _mobileColorPickerPageRoute() {
pageBuilder: (context, state) {
final title =
state.uri.queryParameters[MobileColorPickerScreen.pageTitle] ?? '';
return MaterialPage(
return MaterialExtendedPage(
child: MobileColorPickerScreen(
title: title,
),
@ -285,7 +286,7 @@ GoRoute _mobileImagePickerPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileImagePickerScreen.routeName,
pageBuilder: (context, state) {
return const MaterialPage(
return const MaterialExtendedPage(
child: MobileImagePickerScreen(),
);
},
@ -297,7 +298,7 @@ GoRoute _mobileCodeLanguagePickerPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileCodeLanguagePickerScreen.routeName,
pageBuilder: (context, state) {
return const MaterialPage(
return const MaterialExtendedPage(
child: MobileCodeLanguagePickerScreen(),
);
},
@ -309,7 +310,7 @@ GoRoute _mobileLanguagePickerPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: LanguagePickerScreen.routeName,
pageBuilder: (context, state) {
return const MaterialPage(
return const MaterialExtendedPage(
child: LanguagePickerScreen(),
);
},
@ -321,7 +322,7 @@ GoRoute _mobileFontPickerPageRoute() {
parentNavigatorKey: AppGlobals.rootNavKey,
path: FontPickerScreen.routeName,
pageBuilder: (context, state) {
return const MaterialPage(
return const MaterialExtendedPage(
child: FontPickerScreen(),
);
},
@ -339,7 +340,7 @@ GoRoute _mobileNewPropertyPageRoute() {
state.uri.queryParameters[MobileNewPropertyScreen.argFieldTypeId] ??
FieldType.RichText.value.toString();
final value = int.parse(fieldTypeId);
return MaterialPage(
return MaterialExtendedPage(
fullscreenDialog: true,
child: MobileNewPropertyScreen(
viewId: viewId,
@ -356,7 +357,7 @@ GoRoute _mobileEditPropertyPageRoute() {
path: MobileEditPropertyScreen.routeName,
pageBuilder: (context, state) {
final args = state.extra as Map<String, dynamic>;
return MaterialPage(
return MaterialExtendedPage(
fullscreenDialog: true,
child: MobileEditPropertyScreen(
viewId: args[MobileEditPropertyScreen.argViewId],
@ -374,7 +375,7 @@ GoRoute _mobileCalendarEventsPageRoute() {
pageBuilder: (context, state) {
final args = state.extra as Map<String, dynamic>;
return MaterialPage(
return MaterialExtendedPage(
child: MobileCalendarEventsScreen(
calendarBloc: args[MobileCalendarEventsScreen.calendarBlocKey],
date: args[MobileCalendarEventsScreen.calendarDateKey],
@ -468,7 +469,9 @@ GoRoute _mobileEditorScreenRoute() {
final id = state.uri.queryParameters[MobileEditorScreen.viewId]!;
final title = state.uri.queryParameters[MobileEditorScreen.viewTitle];
return MaterialPage(child: MobileEditorScreen(id: id, title: title));
return MaterialExtendedPage(
child: MobileEditorScreen(id: id, title: title),
);
},
);
}
@ -482,7 +485,7 @@ GoRoute _mobileGridScreenRoute() {
final title = state.uri.queryParameters[MobileGridScreen.viewTitle];
final arguments = state.uri.queryParameters[MobileGridScreen.viewArgs];
return MaterialPage(
return MaterialExtendedPage(
child: MobileGridScreen(
id: id,
title: title,
@ -500,7 +503,7 @@ GoRoute _mobileBoardScreenRoute() {
pageBuilder: (context, state) {
final id = state.uri.queryParameters[MobileBoardScreen.viewId]!;
final title = state.uri.queryParameters[MobileBoardScreen.viewTitle];
return MaterialPage(
return MaterialExtendedPage(
child: MobileBoardScreen(
id: id,
title: title,
@ -517,7 +520,7 @@ GoRoute _mobileCalendarScreenRoute() {
pageBuilder: (context, state) {
final id = state.uri.queryParameters[MobileCalendarScreen.viewId]!;
final title = state.uri.queryParameters[MobileCalendarScreen.viewTitle]!;
return MaterialPage(
return MaterialExtendedPage(
child: MobileCalendarScreen(
id: id,
title: title,
@ -537,7 +540,7 @@ GoRoute _mobileCardDetailScreenRoute() {
args[MobileRowDetailPage.argDatabaseController];
final rowId = args[MobileRowDetailPage.argRowId]!;
return MaterialPage(
return MaterialExtendedPage(
child: MobileRowDetailPage(
databaseController: databaseController,
rowId: rowId,
@ -586,7 +589,7 @@ GoRoute _rootRoute(Widget child) {
},
// Root route is SplashScreen.
// It needs LaunchConfiguration as a parameter, so we get it from ApplicationWidget's child.
pageBuilder: (context, state) => MaterialPage(
pageBuilder: (context, state) => MaterialExtendedPage(
child: child,
),
);

View File

@ -1602,6 +1602,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
sheet:
dependency: "direct main"
description:
path: sheet
ref: "8ee20bd"
resolved-ref: "8ee20bd36acaeb36996a09ba9d0f9e7059bb49df"
url: "https://github.com/richardshiue/modal_bottom_sheet"
source: git
version: "1.0.0-pre"
shelf:
dependency: transitive
description:

View File

@ -129,6 +129,7 @@ dependencies:
scrollable_positioned_list: ^0.3.8
flutter_cache_manager: ^3.3.1
share_plus: ^7.2.1
sheet:
dev_dependencies:
flutter_lints: ^3.0.1
@ -167,6 +168,12 @@ dependency_overrides:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "1715ed4"
sheet:
git:
url: https://github.com/richardshiue/modal_bottom_sheet
ref: 8ee20bd
path: sheet
uuid: ^4.1.0
# The "flutter_lints" package below contains a set of recommended lints to