mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: mobile grid fab (#4093)
This commit is contained in:
parent
04eea26a55
commit
d25830aece
@ -93,20 +93,12 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final view = context.watch<ViewBloc>().state.view;
|
||||
return _buildApp(
|
||||
view,
|
||||
actions,
|
||||
body,
|
||||
);
|
||||
return _buildApp(view, actions, body);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return _buildApp(
|
||||
null,
|
||||
[],
|
||||
body,
|
||||
);
|
||||
return _buildApp(null, [], body);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
@ -26,8 +27,15 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
_startListening();
|
||||
await _openGrid(emit);
|
||||
},
|
||||
createRow: () {
|
||||
databaseController.createRow();
|
||||
createRow: () async {
|
||||
final result = await databaseController.createRow();
|
||||
result.fold(
|
||||
(createdRow) => emit(state.copyWith(createdRow: createdRow)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
resetCreatedRow: () {
|
||||
emit(state.copyWith(createdRow: null));
|
||||
},
|
||||
deleteRow: (rowInfo) async {
|
||||
await RowBackendService.deleteRow(rowInfo.viewId, rowInfo.rowId);
|
||||
@ -142,6 +150,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
class GridEvent with _$GridEvent {
|
||||
const factory GridEvent.initial() = InitialGrid;
|
||||
const factory GridEvent.createRow() = _CreateRow;
|
||||
const factory GridEvent.resetCreatedRow() = _ResetCreatedRow;
|
||||
const factory GridEvent.deleteRow(RowInfo rowInfo) = _DeleteRow;
|
||||
const factory GridEvent.moveRow(int from, int to) = _MoveRow;
|
||||
const factory GridEvent.didLoadRows(
|
||||
@ -170,6 +179,7 @@ class GridState with _$GridState {
|
||||
required FieldList fields,
|
||||
required List<RowInfo> rowInfos,
|
||||
required int rowCount,
|
||||
required RowMetaPB? createdRow,
|
||||
required LoadingState loadingState,
|
||||
required bool reorderable,
|
||||
required ChangedReason reason,
|
||||
@ -181,6 +191,7 @@ class GridState with _$GridState {
|
||||
fields: FieldList([]),
|
||||
rowInfos: [],
|
||||
rowCount: 0,
|
||||
createdRow: null,
|
||||
grid: none(),
|
||||
viewId: viewId,
|
||||
reorderable: true,
|
||||
|
@ -25,6 +25,7 @@ import 'layout/layout.dart';
|
||||
import 'layout/sizes.dart';
|
||||
import 'widgets/footer/grid_footer.dart';
|
||||
import 'widgets/header/grid_header.dart';
|
||||
import 'widgets/mobile_fab.dart';
|
||||
import 'widgets/row/mobile_row.dart';
|
||||
import 'widgets/shortcuts.dart';
|
||||
|
||||
@ -147,23 +148,50 @@ class _GridPageContentState extends State<GridPageContent> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
return BlocConsumer<GridBloc, GridState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.createdRow != current.createdRow,
|
||||
listener: (context, state) {
|
||||
if (state.createdRow == null) {
|
||||
return;
|
||||
}
|
||||
final bloc = context.read<GridBloc>();
|
||||
context.push(
|
||||
MobileRowDetailPage.routeName,
|
||||
extra: {
|
||||
MobileRowDetailPage.argRowId: state.createdRow!.id,
|
||||
MobileRowDetailPage.argDatabaseController: bloc.databaseController,
|
||||
},
|
||||
);
|
||||
bloc.add(const GridEvent.resetCreatedRow());
|
||||
},
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
builder: (context, state) {
|
||||
final contentWidth = GridLayout.headerWidth(state.fields.fields);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
return Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: GridSize.leadingHeaderPadding),
|
||||
child:
|
||||
_GridHeader(headerScrollController: headerScrollController),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(right: GridSize.leadingHeaderPadding),
|
||||
child: _GridHeader(
|
||||
headerScrollController: headerScrollController,
|
||||
),
|
||||
),
|
||||
_GridRows(
|
||||
viewId: state.viewId,
|
||||
contentWidth: contentWidth,
|
||||
scrollController: _scrollController,
|
||||
),
|
||||
],
|
||||
),
|
||||
_GridRows(
|
||||
viewId: state.viewId,
|
||||
contentWidth: contentWidth,
|
||||
scrollController: _scrollController,
|
||||
Positioned(
|
||||
bottom: 20,
|
||||
right: 20,
|
||||
child: getGridFabs(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -0,0 +1,109 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
Widget getGridFabs(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MobileGridFab(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
foregroundColor: Theme.of(context).primaryColor,
|
||||
onTap: () {
|
||||
final bloc = context.read<GridBloc>();
|
||||
if (bloc.state.rowInfos.isNotEmpty) {
|
||||
context.push(
|
||||
MobileRowDetailPage.routeName,
|
||||
extra: {
|
||||
MobileRowDetailPage.argRowId: bloc.state.rowInfos.first.rowId,
|
||||
MobileRowDetailPage.argDatabaseController:
|
||||
bloc.databaseController,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
boxShadow: const BoxShadow(
|
||||
offset: Offset(0, 6),
|
||||
color: Color(0x145D7D8B),
|
||||
blurRadius: 18,
|
||||
),
|
||||
icon: FlowySvgs.properties_s,
|
||||
iconSize: const Size.square(24),
|
||||
),
|
||||
const HSpace(16),
|
||||
MobileGridFab(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
onTap: () {
|
||||
context.read<GridBloc>().add(const GridEvent.createRow());
|
||||
},
|
||||
overlayColor: const MaterialStatePropertyAll<Color>(Color(0xFF009FD1)),
|
||||
boxShadow: const BoxShadow(
|
||||
offset: Offset(0, 8),
|
||||
color: Color(0x6612BFEF),
|
||||
blurRadius: 18,
|
||||
spreadRadius: -5,
|
||||
),
|
||||
icon: FlowySvgs.add_s,
|
||||
iconSize: const Size.square(24),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class MobileGridFab extends StatelessWidget {
|
||||
const MobileGridFab({
|
||||
super.key,
|
||||
required this.backgroundColor,
|
||||
required this.foregroundColor,
|
||||
required this.boxShadow,
|
||||
required this.onTap,
|
||||
required this.icon,
|
||||
required this.iconSize,
|
||||
this.overlayColor,
|
||||
});
|
||||
|
||||
final Color backgroundColor;
|
||||
final Color foregroundColor;
|
||||
final BoxShadow boxShadow;
|
||||
final VoidCallback onTap;
|
||||
final FlowySvgData icon;
|
||||
final Size iconSize;
|
||||
final MaterialStateProperty<Color?>? overlayColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final radius = BorderRadius.circular(20);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: radius,
|
||||
boxShadow: [boxShadow],
|
||||
),
|
||||
child: Material(
|
||||
borderOnForeground: false,
|
||||
color: backgroundColor,
|
||||
borderRadius: radius,
|
||||
child: InkWell(
|
||||
borderRadius: radius,
|
||||
overlayColor: overlayColor,
|
||||
onTap: onTap,
|
||||
child: SizedBox.square(
|
||||
dimension: 56,
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
icon,
|
||||
color: foregroundColor,
|
||||
size: iconSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user