mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: open GridRowActionSheet with appflowy overlay
This commit is contained in:
parent
4d835757d2
commit
53cc0fff9d
@ -87,7 +87,7 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
|||||||
SelectOptionCellEditor.editorPanelWidth,
|
SelectOptionCellEditor.editorPanelWidth,
|
||||||
300,
|
300,
|
||||||
));
|
));
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popover,
|
controller: _popover,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';
|
import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';
|
||||||
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
|
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
|
||||||
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';
|
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';
|
||||||
|
import 'package:appflowy_popover/popover.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'board_cell.dart';
|
import 'board_cell.dart';
|
||||||
import 'card_cell_builder.dart';
|
import 'card_cell_builder.dart';
|
||||||
import 'card_container.dart';
|
import 'container/accessory.dart';
|
||||||
|
import 'container/card_container.dart';
|
||||||
|
|
||||||
class BoardCard extends StatefulWidget {
|
class BoardCard extends StatefulWidget {
|
||||||
final String gridId;
|
final String gridId;
|
||||||
@ -38,6 +40,8 @@ class BoardCard extends StatefulWidget {
|
|||||||
class _BoardCardState extends State<BoardCard> {
|
class _BoardCardState extends State<BoardCard> {
|
||||||
late BoardCardBloc _cardBloc;
|
late BoardCardBloc _cardBloc;
|
||||||
late EditableRowNotifier rowNotifier;
|
late EditableRowNotifier rowNotifier;
|
||||||
|
late PopoverController popoverController;
|
||||||
|
AccessoryType? accessoryType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -54,6 +58,7 @@ class _BoardCardState extends State<BoardCard> {
|
|||||||
_cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value));
|
_cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
popoverController = PopoverController();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,32 +69,42 @@ class _BoardCardState extends State<BoardCard> {
|
|||||||
child: BlocBuilder<BoardCardBloc, BoardCardState>(
|
child: BlocBuilder<BoardCardBloc, BoardCardState>(
|
||||||
buildWhen: (previous, current) {
|
buildWhen: (previous, current) {
|
||||||
// Rebuild when:
|
// Rebuild when:
|
||||||
// 1.If the lenght of the cells is not the same
|
// 1.If the length of the cells is not the same
|
||||||
// 2.isEditing changed
|
// 2.isEditing changed
|
||||||
if (previous.cells.length != current.cells.length ||
|
if (previous.cells.length != current.cells.length ||
|
||||||
previous.isEditing != current.isEditing) {
|
previous.isEditing != current.isEditing) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.Compare the content of the cells. The cells consisits of
|
// 3.Compare the content of the cells. The cells consists of
|
||||||
// list of [BoardCellEquatable] that extends the [Equatable].
|
// list of [BoardCellEquatable] that extends the [Equatable].
|
||||||
return !listEquals(previous.cells, current.cells);
|
return !listEquals(previous.cells, current.cells);
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return BoardCardContainer(
|
return AppFlowyPopover(
|
||||||
buildAccessoryWhen: () => state.isEditing == false,
|
controller: popoverController,
|
||||||
accessoryBuilder: (context) {
|
constraints: BoxConstraints.loose(const Size(140, 200)),
|
||||||
return [
|
direction: PopoverDirection.rightWithCenterAligned,
|
||||||
_CardEditOption(rowNotifier: rowNotifier),
|
popupBuilder: (popoverContext) => _handlePopoverBuilder(
|
||||||
const _CardMoreOption(),
|
context,
|
||||||
];
|
popoverContext,
|
||||||
},
|
),
|
||||||
onTap: (context) => widget.openCard(context),
|
child: BoardCardContainer(
|
||||||
child: _CellColumn(
|
buildAccessoryWhen: () => state.isEditing == false,
|
||||||
groupId: widget.groupId,
|
accessoryBuilder: (context) {
|
||||||
rowNotifier: rowNotifier,
|
return [
|
||||||
cellBuilder: widget.cellBuilder,
|
_CardEditOption(rowNotifier: rowNotifier),
|
||||||
cells: state.cells,
|
_CardMoreOption(),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
openAccessory: _handleOpenAccessory,
|
||||||
|
openCard: (context) => widget.openCard(context),
|
||||||
|
child: _CellColumn(
|
||||||
|
groupId: widget.groupId,
|
||||||
|
rowNotifier: rowNotifier,
|
||||||
|
cellBuilder: widget.cellBuilder,
|
||||||
|
cells: state.cells,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -97,6 +112,30 @@ class _BoardCardState extends State<BoardCard> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleOpenAccessory(AccessoryType newAccessoryType) {
|
||||||
|
accessoryType = newAccessoryType;
|
||||||
|
switch (newAccessoryType) {
|
||||||
|
case AccessoryType.edit:
|
||||||
|
break;
|
||||||
|
case AccessoryType.more:
|
||||||
|
popoverController.show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _handlePopoverBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
BuildContext popoverContext,
|
||||||
|
) {
|
||||||
|
switch (accessoryType!) {
|
||||||
|
case AccessoryType.edit:
|
||||||
|
throw UnimplementedError();
|
||||||
|
case AccessoryType.more:
|
||||||
|
return GridRowActionSheet(
|
||||||
|
rowData: context.read<BoardCardBloc>().rowInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
rowNotifier.dispose();
|
rowNotifier.dispose();
|
||||||
@ -163,7 +202,7 @@ class _CellColumn extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CardMoreOption extends StatelessWidget with CardAccessory {
|
class _CardMoreOption extends StatelessWidget with CardAccessory {
|
||||||
const _CardMoreOption({Key? key}) : super(key: key);
|
_CardMoreOption({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -175,11 +214,7 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onTap(BuildContext context) {
|
AccessoryType get type => AccessoryType.more;
|
||||||
GridRowActionSheet(
|
|
||||||
rowData: context.read<BoardCardBloc>().rowInfo(),
|
|
||||||
).show(context, direction: AnchorDirection.bottomWithCenterAligned);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CardEditOption extends StatelessWidget with CardAccessory {
|
class _CardEditOption extends StatelessWidget with CardAccessory {
|
||||||
@ -201,7 +236,8 @@ class _CardEditOption extends StatelessWidget with CardAccessory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onTap(BuildContext context) {
|
void onTap(BuildContext context) => rowNotifier.becomeFirstResponder();
|
||||||
rowNotifier.becomeFirstResponder();
|
|
||||||
}
|
@override
|
||||||
|
AccessoryType get type => AccessoryType.edit;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flowy_infra/theme.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
enum AccessoryType {
|
||||||
|
edit,
|
||||||
|
more,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CardAccessory implements Widget {
|
||||||
|
AccessoryType get type;
|
||||||
|
void onTap(BuildContext context) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CardAccessoryBuilder = List<CardAccessory> Function(
|
||||||
|
BuildContext buildContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
class CardAccessoryContainer extends StatelessWidget {
|
||||||
|
final void Function(AccessoryType) onTapAccessory;
|
||||||
|
final List<CardAccessory> accessories;
|
||||||
|
const CardAccessoryContainer({
|
||||||
|
required this.accessories,
|
||||||
|
required this.onTapAccessory,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = context.read<AppTheme>();
|
||||||
|
final children = accessories.map((accessory) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
accessory.onTap(context);
|
||||||
|
onTapAccessory(accessory.type);
|
||||||
|
},
|
||||||
|
child: _wrapHover(theme, accessory),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
return _wrapDecoration(context, Row(children: children));
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowyHover _wrapHover(AppTheme theme, CardAccessory accessory) {
|
||||||
|
return FlowyHover(
|
||||||
|
style: HoverStyle(
|
||||||
|
hoverColor: theme.hover,
|
||||||
|
backgroundColor: theme.surface,
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
),
|
||||||
|
builder: (_, onHover) => SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: accessory,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _wrapDecoration(BuildContext context, Widget child) {
|
||||||
|
final theme = context.read<AppTheme>();
|
||||||
|
final borderSide = BorderSide(color: theme.shader6, width: 1.0);
|
||||||
|
final decoration = BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
border: Border.fromBorderSide(borderSide),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
|
);
|
||||||
|
return Container(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: decoration,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,19 @@
|
|||||||
import 'package:flowy_infra/theme.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
import 'accessory.dart';
|
||||||
|
|
||||||
class BoardCardContainer extends StatelessWidget {
|
class BoardCardContainer extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final CardAccessoryBuilder? accessoryBuilder;
|
final CardAccessoryBuilder? accessoryBuilder;
|
||||||
final bool Function()? buildAccessoryWhen;
|
final bool Function()? buildAccessoryWhen;
|
||||||
final void Function(BuildContext) onTap;
|
final void Function(BuildContext) openCard;
|
||||||
|
final void Function(AccessoryType) openAccessory;
|
||||||
const BoardCardContainer({
|
const BoardCardContainer({
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.onTap,
|
required this.openCard,
|
||||||
|
required this.openAccessory,
|
||||||
this.accessoryBuilder,
|
this.accessoryBuilder,
|
||||||
this.buildAccessoryWhen,
|
this.buildAccessoryWhen,
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -34,13 +36,14 @@ class BoardCardContainer extends StatelessWidget {
|
|||||||
if (accessories.isNotEmpty) {
|
if (accessories.isNotEmpty) {
|
||||||
container = _CardEnterRegion(
|
container = _CardEnterRegion(
|
||||||
accessories: accessories,
|
accessories: accessories,
|
||||||
|
onTapAccessory: openAccessory,
|
||||||
child: container,
|
child: container,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => onTap(context),
|
onTap: () => openCard(context),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
@ -55,69 +58,16 @@ class BoardCardContainer extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class CardAccessory implements Widget {
|
|
||||||
void onTap(BuildContext context);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef CardAccessoryBuilder = List<CardAccessory> Function(
|
|
||||||
BuildContext buildContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
class CardAccessoryContainer extends StatelessWidget {
|
|
||||||
final List<CardAccessory> accessories;
|
|
||||||
const CardAccessoryContainer({required this.accessories, Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = context.read<AppTheme>();
|
|
||||||
final children = accessories.map((accessory) {
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => accessory.onTap(context),
|
|
||||||
child: _wrapHover(theme, accessory),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
return _wrapDecoration(context, Row(children: children));
|
|
||||||
}
|
|
||||||
|
|
||||||
FlowyHover _wrapHover(AppTheme theme, CardAccessory accessory) {
|
|
||||||
return FlowyHover(
|
|
||||||
style: HoverStyle(
|
|
||||||
hoverColor: theme.hover,
|
|
||||||
backgroundColor: theme.surface,
|
|
||||||
borderRadius: BorderRadius.zero,
|
|
||||||
),
|
|
||||||
builder: (_, onHover) => SizedBox(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
child: accessory,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _wrapDecoration(BuildContext context, Widget child) {
|
|
||||||
final theme = context.read<AppTheme>();
|
|
||||||
final borderSide = BorderSide(color: theme.shader6, width: 1.0);
|
|
||||||
final decoration = BoxDecoration(
|
|
||||||
color: Colors.transparent,
|
|
||||||
border: Border.fromBorderSide(borderSide),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
|
||||||
);
|
|
||||||
return Container(
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
decoration: decoration,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CardEnterRegion extends StatelessWidget {
|
class _CardEnterRegion extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final List<CardAccessory> accessories;
|
final List<CardAccessory> accessories;
|
||||||
const _CardEnterRegion(
|
final void Function(AccessoryType) onTapAccessory;
|
||||||
{required this.child, required this.accessories, Key? key})
|
const _CardEnterRegion({
|
||||||
: super(key: key);
|
required this.child,
|
||||||
|
required this.accessories,
|
||||||
|
required this.onTapAccessory,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -126,9 +76,12 @@ class _CardEnterRegion extends StatelessWidget {
|
|||||||
builder: (context, onEnter, _) {
|
builder: (context, onEnter, _) {
|
||||||
List<Widget> children = [child];
|
List<Widget> children = [child];
|
||||||
if (onEnter) {
|
if (onEnter) {
|
||||||
children.add(CardAccessoryContainer(
|
children.add(
|
||||||
accessories: accessories,
|
CardAccessoryContainer(
|
||||||
).positioned(right: 0));
|
accessories: accessories,
|
||||||
|
onTapAccessory: onTapAccessory,
|
||||||
|
).positioned(right: 0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MouseRegion(
|
return MouseRegion(
|
@ -62,7 +62,7 @@ class _SettingButtonState extends State<_SettingButton> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.read<AppTheme>();
|
final theme = context.read<AppTheme>();
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: popoverController,
|
controller: popoverController,
|
||||||
constraints: BoxConstraints.loose(const Size(260, 400)),
|
constraints: BoxConstraints.loose(const Size(260, 400)),
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
|
@ -63,7 +63,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
|
|||||||
value: _cellBloc,
|
value: _cellBloc,
|
||||||
child: BlocBuilder<DateCellBloc, DateCellState>(
|
child: BlocBuilder<DateCellBloc, DateCellState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popover,
|
controller: _popover,
|
||||||
offset: const Offset(0, 20),
|
offset: const Offset(0, 20),
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
@ -299,7 +299,7 @@ class _DateTypeOptionButton extends StatelessWidget {
|
|||||||
return BlocSelector<DateCalBloc, DateCalState, DateTypeOptionPB>(
|
return BlocSelector<DateCalBloc, DateCalState, DateTypeOptionPB>(
|
||||||
selector: (state) => state.dateTypeOptionPB,
|
selector: (state) => state.dateTypeOptionPB,
|
||||||
builder: (context, dateTypeOptionPB) {
|
builder: (context, dateTypeOptionPB) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
triggerActions:
|
triggerActions:
|
||||||
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
|
@ -181,7 +181,7 @@ class _SelectOptionWrapState extends State<SelectOptionWrap> {
|
|||||||
SelectOptionCellEditor.editorPanelWidth,
|
SelectOptionCellEditor.editorPanelWidth,
|
||||||
300,
|
300,
|
||||||
));
|
));
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popover,
|
controller: _popover,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
@ -148,11 +148,7 @@ class _TextField extends StatelessWidget {
|
|||||||
selectedOptionMap: optionMap,
|
selectedOptionMap: optionMap,
|
||||||
distanceToText: _editorPanelWidth * 0.7,
|
distanceToText: _editorPanelWidth * 0.7,
|
||||||
tagController: _tagController,
|
tagController: _tagController,
|
||||||
onClick: () {
|
onClick: () => popoverMutex.close(),
|
||||||
popoverMutex.close();
|
|
||||||
// FlowyOverlay.of(context)
|
|
||||||
// .remove(SelectOptionTypeOptionEditor.identifier);
|
|
||||||
},
|
|
||||||
newText: (text) {
|
newText: (text) {
|
||||||
context
|
context
|
||||||
.read<SelectOptionCellEditorBloc>()
|
.read<SelectOptionCellEditorBloc>()
|
||||||
@ -244,7 +240,7 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popoverController,
|
controller: _popoverController,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
constraints: BoxConstraints.loose(const Size(200, 300)),
|
constraints: BoxConstraints.loose(const Size(200, 300)),
|
||||||
|
@ -130,7 +130,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popoverController,
|
controller: _popoverController,
|
||||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
constraints: BoxConstraints.loose(const Size(300, 160)),
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
@ -216,7 +216,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
constraints: BoxConstraints.loose(const Size(300, 160)),
|
||||||
controller: _popoverController,
|
controller: _popoverController,
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
@ -30,7 +30,7 @@ class GridFieldCell extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: BlocBuilder<FieldCellBloc, FieldCellState>(
|
child: BlocBuilder<FieldCellBloc, FieldCellState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final button = AppFlowyStylePopover(
|
final button = AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(240, 840)),
|
constraints: BoxConstraints.loose(const Size(240, 840)),
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
|
@ -243,7 +243,7 @@ class _DeleteFieldButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _wrapPopover(Widget widget) {
|
Widget _wrapPopover(Widget widget) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
constraints: BoxConstraints.loose(const Size(400, 240)),
|
constraints: BoxConstraints.loose(const Size(400, 240)),
|
||||||
mutex: popoverMutex,
|
mutex: popoverMutex,
|
||||||
|
@ -64,7 +64,7 @@ class FieldTypeOptionEditor extends StatelessWidget {
|
|||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: GridSize.typeOptionItemHeight,
|
height: GridSize.typeOptionItemHeight,
|
||||||
child: AppFlowyStylePopover(
|
child: AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(460, 440)),
|
constraints: BoxConstraints.loose(const Size(460, 440)),
|
||||||
triggerActions:
|
triggerActions:
|
||||||
PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,
|
PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,
|
||||||
|
@ -176,7 +176,7 @@ class CreateFieldButton extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
|
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
direction: PopoverDirection.bottomWithRightAligned,
|
direction: PopoverDirection.bottomWithRightAligned,
|
||||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||||
|
@ -62,7 +62,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
|
Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
mutex: popoverMutex,
|
mutex: popoverMutex,
|
||||||
triggerActions:
|
triggerActions:
|
||||||
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
||||||
@ -84,7 +84,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
|
Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
mutex: popoverMutex,
|
mutex: popoverMutex,
|
||||||
triggerActions:
|
triggerActions:
|
||||||
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
|
||||||
|
@ -55,7 +55,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
|
|||||||
listener: (context, state) =>
|
listener: (context, state) =>
|
||||||
typeOptionContext.typeOption = state.typeOption,
|
typeOptionContext.typeOption = state.typeOption,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
mutex: popoverMutex,
|
mutex: popoverMutex,
|
||||||
triggerActions: PopoverTriggerActionFlags.hover |
|
triggerActions: PopoverTriggerActionFlags.hover |
|
||||||
PopoverTriggerActionFlags.click,
|
PopoverTriggerActionFlags.click,
|
||||||
|
@ -180,7 +180,7 @@ class _OptionCellState extends State<_OptionCell> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
|
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
controller: _popoverController,
|
controller: _popoverController,
|
||||||
mutex: widget.popoverMutex,
|
mutex: widget.popoverMutex,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'package:app_flowy/plugins/grid/application/prelude.dart';
|
import 'package:app_flowy/plugins/grid/application/prelude.dart';
|
||||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||||
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
|
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
|
||||||
|
import 'package:appflowy_popover/popover.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -56,20 +58,21 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
|||||||
child: BlocBuilder<RowBloc, RowState>(
|
child: BlocBuilder<RowBloc, RowState>(
|
||||||
buildWhen: (p, c) => p.rowInfo.rowPB.height != c.rowInfo.rowPB.height,
|
buildWhen: (p, c) => p.rowInfo.rowPB.height != c.rowInfo.rowPB.height,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final children = [
|
final content = Expanded(
|
||||||
const _RowLeading(),
|
child: RowContent(
|
||||||
Expanded(
|
builder: widget.cellBuilder,
|
||||||
child: RowContent(
|
onExpand: () => widget.openDetailPage(
|
||||||
builder: widget.cellBuilder,
|
context,
|
||||||
onExpand: () => widget.openDetailPage(
|
widget.cellBuilder,
|
||||||
context,
|
|
||||||
widget.cellBuilder,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Row(children: [
|
||||||
|
const _RowLeading(),
|
||||||
|
content,
|
||||||
const _RowTrailing(),
|
const _RowTrailing(),
|
||||||
];
|
]);
|
||||||
return Row(children: children);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -83,26 +86,51 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RowLeading extends StatelessWidget {
|
class _RowLeading extends StatefulWidget {
|
||||||
const _RowLeading({Key? key}) : super(key: key);
|
const _RowLeading({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_RowLeading> createState() => _RowLeadingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RowLeadingState extends State<_RowLeading> {
|
||||||
|
late PopoverController popoverController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
popoverController = PopoverController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<RegionStateNotifier>(
|
return AppFlowyPopover(
|
||||||
builder: (context, state, _) {
|
controller: popoverController,
|
||||||
return SizedBox(
|
constraints: BoxConstraints.loose(const Size(140, 200)),
|
||||||
width: GridSize.leadingHeaderPadding,
|
direction: PopoverDirection.rightWithCenterAligned,
|
||||||
child: state.onEnter ? _activeWidget() : null);
|
popupBuilder: (BuildContext popoverContext) {
|
||||||
|
return GridRowActionSheet(
|
||||||
|
rowData: context.read<RowBloc>().state.rowInfo);
|
||||||
},
|
},
|
||||||
|
child: Consumer<RegionStateNotifier>(
|
||||||
|
builder: (context, state, _) {
|
||||||
|
return SizedBox(
|
||||||
|
width: GridSize.leadingHeaderPadding,
|
||||||
|
child: state.onEnter ? _activeWidget() : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _activeWidget() {
|
Widget _activeWidget() {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: [
|
||||||
_InsertRowButton(),
|
const _InsertButton(),
|
||||||
_DeleteRowButton(),
|
_MenuButton(openMenu: () {
|
||||||
|
popoverController.show();
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -117,8 +145,8 @@ class _RowTrailing extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InsertRowButton extends StatelessWidget {
|
class _InsertButton extends StatelessWidget {
|
||||||
const _InsertRowButton({Key? key}) : super(key: key);
|
const _InsertButton({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -128,17 +156,29 @@ class _InsertRowButton extends StatelessWidget {
|
|||||||
hoverColor: theme.hover,
|
hoverColor: theme.hover,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 30,
|
height: 30,
|
||||||
onPressed: () => context.read<RowBloc>().add(
|
onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
|
||||||
const RowEvent.createRow(),
|
|
||||||
),
|
|
||||||
iconPadding: const EdgeInsets.all(3),
|
iconPadding: const EdgeInsets.all(3),
|
||||||
icon: svgWidget("home/add"),
|
icon: svgWidget("home/add"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DeleteRowButton extends StatelessWidget {
|
class _MenuButton extends StatefulWidget {
|
||||||
const _DeleteRowButton({Key? key}) : super(key: key);
|
final VoidCallback openMenu;
|
||||||
|
const _MenuButton({
|
||||||
|
required this.openMenu,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_MenuButton> createState() => _MenuButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MenuButtonState extends State<_MenuButton> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -148,9 +188,7 @@ class _DeleteRowButton extends StatelessWidget {
|
|||||||
hoverColor: theme.hover,
|
hoverColor: theme.hover,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 30,
|
height: 30,
|
||||||
onPressed: () => GridRowActionSheet(
|
onPressed: () => widget.openMenu(),
|
||||||
rowData: context.read<RowBloc>().state.rowInfo,
|
|
||||||
).show(context),
|
|
||||||
iconPadding: const EdgeInsets.all(3),
|
iconPadding: const EdgeInsets.all(3),
|
||||||
icon: svgWidget("editor/details"),
|
icon: svgWidget("editor/details"),
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
@ -27,10 +26,7 @@ class GridRowActionSheet extends StatelessWidget {
|
|||||||
final cells = _RowAction.values
|
final cells = _RowAction.values
|
||||||
.where((value) => value.enable())
|
.where((value) => value.enable())
|
||||||
.map(
|
.map(
|
||||||
(action) => _RowActionCell(
|
(action) => _RowActionCell(action: action),
|
||||||
action: action,
|
|
||||||
onDismissed: () => remove(context),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@ -52,37 +48,11 @@ class GridRowActionSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show(
|
|
||||||
BuildContext overlayContext, {
|
|
||||||
AnchorDirection direction = AnchorDirection.leftWithCenterAligned,
|
|
||||||
}) {
|
|
||||||
FlowyOverlay.of(overlayContext).insertWithAnchor(
|
|
||||||
widget: OverlayContainer(
|
|
||||||
constraints: BoxConstraints.loose(const Size(140, 200)),
|
|
||||||
child: this,
|
|
||||||
),
|
|
||||||
identifier: GridRowActionSheet.identifier(),
|
|
||||||
anchorContext: overlayContext,
|
|
||||||
anchorDirection: direction,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(BuildContext overlayContext) {
|
|
||||||
FlowyOverlay.of(overlayContext).remove(GridRowActionSheet.identifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
static String identifier() {
|
|
||||||
return (GridRowActionSheet).toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RowActionCell extends StatelessWidget {
|
class _RowActionCell extends StatelessWidget {
|
||||||
final _RowAction action;
|
final _RowAction action;
|
||||||
final VoidCallback onDismissed;
|
const _RowActionCell({required this.action, Key? key}) : super(key: key);
|
||||||
const _RowActionCell(
|
|
||||||
{required this.action, required this.onDismissed, Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -101,7 +71,6 @@ class _RowActionCell extends StatelessWidget {
|
|||||||
if (action.enable()) {
|
if (action.enable()) {
|
||||||
action.performAction(context);
|
action.performAction(context);
|
||||||
}
|
}
|
||||||
onDismissed();
|
|
||||||
},
|
},
|
||||||
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
|
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
|
||||||
),
|
),
|
||||||
|
@ -194,7 +194,7 @@ class _CreateFieldButtonState extends State<_CreateFieldButton> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.read<AppTheme>();
|
final theme = context.read<AppTheme>();
|
||||||
|
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||||
controller: popoverController,
|
controller: popoverController,
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
|
@ -99,7 +99,6 @@ class _GridGroupCell extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
onSelected();
|
onSelected();
|
||||||
// FlowyOverlay.of(context).remove(GridGroupList.identifier());
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _editFieldButton(AppTheme theme, BuildContext context) {
|
Widget _editFieldButton(AppTheme theme, BuildContext context) {
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
mutex: popoverMutex,
|
mutex: popoverMutex,
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
|
@ -40,7 +40,6 @@ class GridSettingList extends StatelessWidget {
|
|||||||
previous.selectedAction != current.selectedAction,
|
previous.selectedAction != current.selectedAction,
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
state.selectedAction.foldLeft(null, (_, action) {
|
state.selectedAction.foldLeft(null, (_, action) {
|
||||||
FlowyOverlay.of(context).remove(identifier());
|
|
||||||
onAction(action, settingContext);
|
onAction(action, settingContext);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -53,7 +53,7 @@ class _SettingButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return AppFlowyStylePopover(
|
return AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(260, 400)),
|
constraints: BoxConstraints.loose(const Size(260, 400)),
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
offset: const Offset(0, 10),
|
offset: const Offset(0, 10),
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
|||||||
import 'package:appflowy_popover/popover.dart';
|
import 'package:appflowy_popover/popover.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppFlowyStylePopover extends StatelessWidget {
|
class AppFlowyPopover extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final PopoverController? controller;
|
final PopoverController? controller;
|
||||||
final Widget Function(BuildContext context) popupBuilder;
|
final Widget Function(BuildContext context) popupBuilder;
|
||||||
@ -13,7 +13,7 @@ class AppFlowyStylePopover extends StatelessWidget {
|
|||||||
final PopoverMutex? mutex;
|
final PopoverMutex? mutex;
|
||||||
final Offset? offset;
|
final Offset? offset;
|
||||||
|
|
||||||
const AppFlowyStylePopover({
|
const AppFlowyPopover({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.popupBuilder,
|
required this.popupBuilder,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user