mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: bottom sheets with transition (#4608)
* feat: shiny new bottom sheets * chore: code review
This commit is contained in:
@ -3,7 +3,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.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);
|
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
|
||||||
|
|
||||||
|
@ -7,3 +7,4 @@ export 'bottom_sheet_view_item_body.dart';
|
|||||||
export 'bottom_sheet_view_page.dart';
|
export 'bottom_sheet_view_page.dart';
|
||||||
export 'default_mobile_action_pane.dart';
|
export 'default_mobile_action_pane.dart';
|
||||||
export 'show_mobile_bottom_sheet.dart';
|
export 'show_mobile_bottom_sheet.dart';
|
||||||
|
export 'show_transition_bottom_sheet.dart';
|
||||||
|
@ -79,7 +79,7 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
|
|
||||||
if (showHeader) {
|
if (showHeader) {
|
||||||
children.add(
|
children.add(
|
||||||
_Header(
|
BottomSheetHeader(
|
||||||
showCloseButton: showCloseButton,
|
showCloseButton: showCloseButton,
|
||||||
showBackButton: showBackButton,
|
showBackButton: showBackButton,
|
||||||
showDoneButton: showDoneButton,
|
showDoneButton: showDoneButton,
|
||||||
@ -152,8 +152,9 @@ Future<T?> showMobileBottomSheet<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Header extends StatelessWidget {
|
class BottomSheetHeader extends StatelessWidget {
|
||||||
const _Header({
|
const BottomSheetHeader({
|
||||||
|
super.key,
|
||||||
required this.showBackButton,
|
required this.showBackButton,
|
||||||
required this.showCloseButton,
|
required this.showCloseButton,
|
||||||
required this.title,
|
required this.title,
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,6 @@ void showCreateFieldBottomSheet(
|
|||||||
elevation: 20,
|
elevation: 20,
|
||||||
title: LocaleKeys.grid_field_newProperty.tr(),
|
title: LocaleKeys.grid_field_newProperty.tr(),
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
enableDraggableScrollable: true,
|
enableDraggableScrollable: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
||||||
|
@ -447,14 +447,12 @@ class _PropertyType extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
showCloseButton: true,
|
showCloseButton: true,
|
||||||
elevation: 20,
|
elevation: 20,
|
||||||
title: LocaleKeys.grid_field_editProperty.tr(),
|
title: LocaleKeys.grid_field_editProperty.tr(),
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
barrierColor: Colors.transparent,
|
|
||||||
enableDraggableScrollable: true,
|
enableDraggableScrollable: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
||||||
|
@ -68,7 +68,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
fieldController: databaseController.fieldController,
|
fieldController: databaseController.fieldController,
|
||||||
fieldInfo: firstField,
|
fieldInfo: firstField,
|
||||||
showTopBorder: true,
|
showTopBorder: false,
|
||||||
);
|
);
|
||||||
final cells = fields
|
final cells = fields
|
||||||
.mapIndexed(
|
.mapIndexed(
|
||||||
@ -124,9 +124,10 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_divider(),
|
_divider(),
|
||||||
_NewDatabaseFieldTile(viewId: viewId),
|
_NewDatabaseFieldTile(viewId: viewId),
|
||||||
|
const VSpace(24),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: const VSpace(24),
|
||||||
itemCount: cells.length,
|
itemCount: cells.length,
|
||||||
itemBuilder: (context, index) => cells[index],
|
itemBuilder: (context, index) => cells[index],
|
||||||
);
|
);
|
||||||
|
@ -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/base/app_bar_actions.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.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/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
|
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
@ -27,50 +26,39 @@ class MobileDatabaseViewList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DraggableScrollableSheet(
|
return BlocBuilder<ViewBloc, ViewState>(
|
||||||
expand: false,
|
builder: (context, state) {
|
||||||
snap: true,
|
final views = [state.view, ...state.view.childViews];
|
||||||
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(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const DragHandle(),
|
_Header(
|
||||||
_Header(
|
title: LocaleKeys.grid_settings_viewList.plural(
|
||||||
title: LocaleKeys.grid_settings_viewList.plural(
|
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
|
||||||
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
|
namedArgs: {
|
||||||
namedArgs: {
|
'count':
|
||||||
'count':
|
'${context.watch<DatabaseTabBarBloc>().state.tabBars.length}',
|
||||||
'${context.watch<DatabaseTabBarBloc>().state.tabBars.length}',
|
},
|
||||||
},
|
),
|
||||||
|
showBackButton: false,
|
||||||
|
useFilledDoneButton: false,
|
||||||
|
onDone: (context) => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...views.mapIndexed(
|
||||||
|
(index, view) => MobileDatabaseViewListButton(
|
||||||
|
view: view,
|
||||||
|
showTopBorder: index == 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
showBackButton: false,
|
const VSpace(20),
|
||||||
useFilledDoneButton: false,
|
const MobileNewDatabaseViewButton(),
|
||||||
onDone: (context) => Navigator.pop(context),
|
],
|
||||||
),
|
),
|
||||||
SingleChildScrollView(
|
),
|
||||||
controller: scrollController,
|
],
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
...views.mapIndexed(
|
|
||||||
(index, view) => MobileDatabaseViewListButton(
|
|
||||||
view: view,
|
|
||||||
showTopBorder: index == 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const VSpace(20),
|
|
||||||
const MobileNewDatabaseViewButton(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.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/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
@ -33,15 +33,11 @@ class MobileDatabaseViewQuickActions extends StatelessWidget {
|
|||||||
_actionButton(context, _Action.edit, () {
|
_actionButton(context, _Action.edit, () {
|
||||||
final bloc = context.read<ViewBloc>();
|
final bloc = context.read<ViewBloc>();
|
||||||
context.pop();
|
context.pop();
|
||||||
showMobileBottomSheet(
|
showTransitionMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showDoneButton: true,
|
showDoneButton: true,
|
||||||
title: LocaleKeys.grid_settings_editView.tr(),
|
title: LocaleKeys.grid_settings_editView.tr(),
|
||||||
enableDraggableScrollable: true,
|
|
||||||
initialChildSize: 0.98,
|
|
||||||
minChildSize: 0.98,
|
|
||||||
maxChildSize: 0.98,
|
|
||||||
builder: (_) => BlocProvider.value(
|
builder: (_) => BlocProvider.value(
|
||||||
value: bloc,
|
value: bloc,
|
||||||
child: MobileEditDatabaseViewScreen(
|
child: MobileEditDatabaseViewScreen(
|
||||||
|
@ -33,8 +33,7 @@ class MobileEditDatabaseViewScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ViewBloc, ViewState>(
|
return BlocBuilder<ViewBloc, ViewState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ListView(
|
return Column(
|
||||||
shrinkWrap: true,
|
|
||||||
children: [
|
children: [
|
||||||
_NameAndIcon(view: state.view),
|
_NameAndIcon(view: state.view),
|
||||||
_divider(),
|
_divider(),
|
||||||
@ -91,13 +90,15 @@ class _NameAndIconState extends State<_NameAndIcon> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlowyOptionTile.textField(
|
return Material(
|
||||||
autofocus: true,
|
child: FlowyOptionTile.textField(
|
||||||
showTopBorder: false,
|
autofocus: true,
|
||||||
controller: textEditingController,
|
showTopBorder: false,
|
||||||
onTextChanged: (text) {
|
controller: textEditingController,
|
||||||
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
onTextChanged: (text) {
|
||||||
},
|
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,18 +229,12 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setting == DatabaseViewSettings.fields) {
|
if (setting == DatabaseViewSettings.fields) {
|
||||||
await showMobileBottomSheet(
|
await showTransitionMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
useSafeArea: false,
|
|
||||||
showDragHandle: true,
|
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
title: LocaleKeys.grid_settings_properties.tr(),
|
title: LocaleKeys.grid_settings_properties.tr(),
|
||||||
showDivider: true,
|
showDivider: true,
|
||||||
enableDraggableScrollable: true,
|
|
||||||
initialChildSize: 0.98,
|
|
||||||
minChildSize: 0.98,
|
|
||||||
maxChildSize: 0.98,
|
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: context.read<ViewBloc>(),
|
value: context.read<ViewBloc>(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
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/mobile/presentation/database/view/database_view_list.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||||
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
|
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
|
||||||
@ -108,9 +108,10 @@ class _DatabaseViewSelectorButton extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showMobileBottomSheet(
|
showTransitionMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDivider: false,
|
showDivider: false,
|
||||||
|
initialStop: 1.0,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -58,9 +58,8 @@ class MobileDatabaseControls extends StatelessWidget {
|
|||||||
|
|
||||||
return _DatabaseControlButton(
|
return _DatabaseControlButton(
|
||||||
icon: FlowySvgs.m_field_hide_s,
|
icon: FlowySvgs.m_field_hide_s,
|
||||||
onTap: () => showMobileBottomSheet(
|
onTap: () => showTransitionMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
title: LocaleKeys.grid_settings_properties.tr(),
|
title: LocaleKeys.grid_settings_properties.tr(),
|
||||||
|
@ -30,6 +30,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
|||||||
import 'package:flowy_infra/time/duration.dart';
|
import 'package:flowy_infra/time/duration.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:sheet/route.dart';
|
||||||
|
|
||||||
GoRouter generateRouter(Widget child) {
|
GoRouter generateRouter(Widget child) {
|
||||||
return GoRouter(
|
return GoRouter(
|
||||||
@ -193,7 +194,7 @@ GoRoute _mobileHomeSettingPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: MobileHomeSettingPage.routeName,
|
path: MobileHomeSettingPage.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(child: MobileHomeSettingPage());
|
return const MaterialExtendedPage(child: MobileHomeSettingPage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -203,7 +204,7 @@ GoRoute _mobileCloudSettingAppFlowyCloudPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: AppFlowyCloudPage.routeName,
|
path: AppFlowyCloudPage.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(child: AppFlowyCloudPage());
|
return const MaterialExtendedPage(child: AppFlowyCloudPage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -213,7 +214,7 @@ GoRoute _mobileLaunchSettingsPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: MobileLaunchSettingsPage.routeName,
|
path: MobileLaunchSettingsPage.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(child: MobileLaunchSettingsPage());
|
return const MaterialExtendedPage(child: MobileLaunchSettingsPage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -223,7 +224,7 @@ GoRoute _mobileHomeTrashPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: MobileHomeTrashPage.routeName,
|
path: MobileHomeTrashPage.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(child: MobileHomeTrashPage());
|
return const MaterialExtendedPage(child: MobileHomeTrashPage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -239,7 +240,7 @@ GoRoute _mobileBlockSettingsPageRoute() {
|
|||||||
?.split(',')
|
?.split(',')
|
||||||
.map(MobileBlockActionType.fromActionString)
|
.map(MobileBlockActionType.fromActionString)
|
||||||
.toList();
|
.toList();
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileBlockSettingsScreen(
|
child: MobileBlockSettingsScreen(
|
||||||
actions: actions ?? MobileBlockActionType.standard,
|
actions: actions ?? MobileBlockActionType.standard,
|
||||||
),
|
),
|
||||||
@ -255,7 +256,7 @@ GoRoute _mobileEmojiPickerPageRoute() {
|
|||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final title =
|
final title =
|
||||||
state.uri.queryParameters[MobileEmojiPickerScreen.pageTitle];
|
state.uri.queryParameters[MobileEmojiPickerScreen.pageTitle];
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileEmojiPickerScreen(
|
child: MobileEmojiPickerScreen(
|
||||||
title: title,
|
title: title,
|
||||||
),
|
),
|
||||||
@ -271,7 +272,7 @@ GoRoute _mobileColorPickerPageRoute() {
|
|||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final title =
|
final title =
|
||||||
state.uri.queryParameters[MobileColorPickerScreen.pageTitle] ?? '';
|
state.uri.queryParameters[MobileColorPickerScreen.pageTitle] ?? '';
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileColorPickerScreen(
|
child: MobileColorPickerScreen(
|
||||||
title: title,
|
title: title,
|
||||||
),
|
),
|
||||||
@ -285,7 +286,7 @@ GoRoute _mobileImagePickerPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: MobileImagePickerScreen.routeName,
|
path: MobileImagePickerScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(
|
return const MaterialExtendedPage(
|
||||||
child: MobileImagePickerScreen(),
|
child: MobileImagePickerScreen(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -297,7 +298,7 @@ GoRoute _mobileCodeLanguagePickerPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: MobileCodeLanguagePickerScreen.routeName,
|
path: MobileCodeLanguagePickerScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(
|
return const MaterialExtendedPage(
|
||||||
child: MobileCodeLanguagePickerScreen(),
|
child: MobileCodeLanguagePickerScreen(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -309,7 +310,7 @@ GoRoute _mobileLanguagePickerPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: LanguagePickerScreen.routeName,
|
path: LanguagePickerScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(
|
return const MaterialExtendedPage(
|
||||||
child: LanguagePickerScreen(),
|
child: LanguagePickerScreen(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -321,7 +322,7 @@ GoRoute _mobileFontPickerPageRoute() {
|
|||||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||||
path: FontPickerScreen.routeName,
|
path: FontPickerScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return const MaterialPage(
|
return const MaterialExtendedPage(
|
||||||
child: FontPickerScreen(),
|
child: FontPickerScreen(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -339,7 +340,7 @@ GoRoute _mobileNewPropertyPageRoute() {
|
|||||||
state.uri.queryParameters[MobileNewPropertyScreen.argFieldTypeId] ??
|
state.uri.queryParameters[MobileNewPropertyScreen.argFieldTypeId] ??
|
||||||
FieldType.RichText.value.toString();
|
FieldType.RichText.value.toString();
|
||||||
final value = int.parse(fieldTypeId);
|
final value = int.parse(fieldTypeId);
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
child: MobileNewPropertyScreen(
|
child: MobileNewPropertyScreen(
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
@ -356,7 +357,7 @@ GoRoute _mobileEditPropertyPageRoute() {
|
|||||||
path: MobileEditPropertyScreen.routeName,
|
path: MobileEditPropertyScreen.routeName,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final args = state.extra as Map<String, dynamic>;
|
final args = state.extra as Map<String, dynamic>;
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
child: MobileEditPropertyScreen(
|
child: MobileEditPropertyScreen(
|
||||||
viewId: args[MobileEditPropertyScreen.argViewId],
|
viewId: args[MobileEditPropertyScreen.argViewId],
|
||||||
@ -374,7 +375,7 @@ GoRoute _mobileCalendarEventsPageRoute() {
|
|||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final args = state.extra as Map<String, dynamic>;
|
final args = state.extra as Map<String, dynamic>;
|
||||||
|
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileCalendarEventsScreen(
|
child: MobileCalendarEventsScreen(
|
||||||
calendarBloc: args[MobileCalendarEventsScreen.calendarBlocKey],
|
calendarBloc: args[MobileCalendarEventsScreen.calendarBlocKey],
|
||||||
date: args[MobileCalendarEventsScreen.calendarDateKey],
|
date: args[MobileCalendarEventsScreen.calendarDateKey],
|
||||||
@ -468,7 +469,9 @@ GoRoute _mobileEditorScreenRoute() {
|
|||||||
final id = state.uri.queryParameters[MobileEditorScreen.viewId]!;
|
final id = state.uri.queryParameters[MobileEditorScreen.viewId]!;
|
||||||
final title = state.uri.queryParameters[MobileEditorScreen.viewTitle];
|
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 title = state.uri.queryParameters[MobileGridScreen.viewTitle];
|
||||||
final arguments = state.uri.queryParameters[MobileGridScreen.viewArgs];
|
final arguments = state.uri.queryParameters[MobileGridScreen.viewArgs];
|
||||||
|
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileGridScreen(
|
child: MobileGridScreen(
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
@ -500,7 +503,7 @@ GoRoute _mobileBoardScreenRoute() {
|
|||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final id = state.uri.queryParameters[MobileBoardScreen.viewId]!;
|
final id = state.uri.queryParameters[MobileBoardScreen.viewId]!;
|
||||||
final title = state.uri.queryParameters[MobileBoardScreen.viewTitle];
|
final title = state.uri.queryParameters[MobileBoardScreen.viewTitle];
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileBoardScreen(
|
child: MobileBoardScreen(
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
@ -517,7 +520,7 @@ GoRoute _mobileCalendarScreenRoute() {
|
|||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final id = state.uri.queryParameters[MobileCalendarScreen.viewId]!;
|
final id = state.uri.queryParameters[MobileCalendarScreen.viewId]!;
|
||||||
final title = state.uri.queryParameters[MobileCalendarScreen.viewTitle]!;
|
final title = state.uri.queryParameters[MobileCalendarScreen.viewTitle]!;
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileCalendarScreen(
|
child: MobileCalendarScreen(
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
@ -537,7 +540,7 @@ GoRoute _mobileCardDetailScreenRoute() {
|
|||||||
args[MobileRowDetailPage.argDatabaseController];
|
args[MobileRowDetailPage.argDatabaseController];
|
||||||
final rowId = args[MobileRowDetailPage.argRowId]!;
|
final rowId = args[MobileRowDetailPage.argRowId]!;
|
||||||
|
|
||||||
return MaterialPage(
|
return MaterialExtendedPage(
|
||||||
child: MobileRowDetailPage(
|
child: MobileRowDetailPage(
|
||||||
databaseController: databaseController,
|
databaseController: databaseController,
|
||||||
rowId: rowId,
|
rowId: rowId,
|
||||||
@ -586,7 +589,7 @@ GoRoute _rootRoute(Widget child) {
|
|||||||
},
|
},
|
||||||
// Root route is SplashScreen.
|
// Root route is SplashScreen.
|
||||||
// It needs LaunchConfiguration as a parameter, so we get it from ApplicationWidget's child.
|
// It needs LaunchConfiguration as a parameter, so we get it from ApplicationWidget's child.
|
||||||
pageBuilder: (context, state) => MaterialPage(
|
pageBuilder: (context, state) => MaterialExtendedPage(
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1602,6 +1602,15 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
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:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -129,6 +129,7 @@ dependencies:
|
|||||||
scrollable_positioned_list: ^0.3.8
|
scrollable_positioned_list: ^0.3.8
|
||||||
flutter_cache_manager: ^3.3.1
|
flutter_cache_manager: ^3.3.1
|
||||||
share_plus: ^7.2.1
|
share_plus: ^7.2.1
|
||||||
|
sheet:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^3.0.1
|
flutter_lints: ^3.0.1
|
||||||
@ -167,6 +168,12 @@ dependency_overrides:
|
|||||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||||
ref: "1715ed4"
|
ref: "1715ed4"
|
||||||
|
|
||||||
|
sheet:
|
||||||
|
git:
|
||||||
|
url: https://github.com/richardshiue/modal_bottom_sheet
|
||||||
|
ref: 8ee20bd
|
||||||
|
path: sheet
|
||||||
|
|
||||||
uuid: ^4.1.0
|
uuid: ^4.1.0
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
|
Reference in New Issue
Block a user