test: filter integration (#2821)

* test: add checklist filter test

* fix: widget reference to invalid databaseController

* fix: SelectOptionFilterList doesn't expand to fill the CustomScrollView

* test: add single select and multi-select filter test

* ci: set protoc version
This commit is contained in:
Nathan.fooo
2023-06-16 14:32:32 +08:00
committed by GitHub
parent 63eee8b415
commit 14dee6b797
10 changed files with 239 additions and 72 deletions

View File

@ -1,3 +1,5 @@
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
@ -45,14 +47,14 @@ void main() {
await tester.assertNumberOfRowsInGridPage(1); await tester.assertNumberOfRowsInGridPage(1);
// open the menu to delete the filter // open the menu to delete the filter
await tester.tapTextFilterDisclosureButtonInGrid(); await tester.tapDisclosureButtonInFinder(find.byType(TextFilterEditor));
await tester.tapDeleteTextFilterButtonInGrid(); await tester.tapDeleteFilterButtonInGrid();
await tester.assertNumberOfRowsInGridPage(10); await tester.assertNumberOfRowsInGridPage(10);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('add checklist filter', (tester) async { testWidgets('add checkbox filter', (tester) async {
await tester.openV020database(); await tester.openV020database();
// create a filter // create a filter
@ -66,6 +68,90 @@ void main() {
await tester.tapUnCheckedButtonOnCheckboxFilter(); await tester.tapUnCheckedButtonOnCheckboxFilter();
await tester.assertNumberOfRowsInGridPage(5); await tester.assertNumberOfRowsInGridPage(5);
await tester
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
await tester.tapDeleteFilterButtonInGrid();
await tester.assertNumberOfRowsInGridPage(10);
await tester.pumpAndSettle();
});
testWidgets('add checklist filter', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
await tester.tapCreateFilterByFieldType(FieldType.Checklist, 'checklist');
// By default, the condition of checklist filter is 'uncompleted'
await tester.assertNumberOfRowsInGridPage(9);
await tester.tapFilterButtonInGrid('checklist');
await tester.tapChecklistFilterButtonInGrid();
await tester.tapCompletedButtonOnChecklistFilter();
await tester.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle();
});
testWidgets('add single select filter', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
await tester.tapCreateFilterByFieldType(FieldType.SingleSelect, 'Type');
await tester.tapFilterButtonInGrid('Type');
// select the option 's6'
await tester.tapOptionFilterWithName('s6');
await tester.assertNumberOfRowsInGridPage(0);
// unselect the option 's6'
await tester.tapOptionFilterWithName('s6');
await tester.assertNumberOfRowsInGridPage(10);
// select the option 's5'
await tester.tapOptionFilterWithName('s5');
await tester.assertNumberOfRowsInGridPage(1);
// select the option 's4'
await tester.tapOptionFilterWithName('s4');
// The row with 's4' or 's5' should be shown.
await tester.assertNumberOfRowsInGridPage(2);
await tester.pumpAndSettle();
});
testWidgets('add multi select filter', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
await tester.tapCreateFilterByFieldType(
FieldType.MultiSelect,
'multi-select',
);
await tester.tapFilterButtonInGrid('multi-select');
await tester.scrollOptionFilterListByOffset(const Offset(0, -200));
// select the option 'm1'. Any option with 'm1' should be shown.
await tester.tapOptionFilterWithName('m1');
await tester.assertNumberOfRowsInGridPage(5);
await tester.tapOptionFilterWithName('m1');
// select the option 'm2'. Any option with 'm2' should be shown.
await tester.tapOptionFilterWithName('m2');
await tester.assertNumberOfRowsInGridPage(4);
await tester.tapOptionFilterWithName('m2');
// select the option 'm4'. Any option with 'm4' should be shown.
await tester.tapOptionFilterWithName('m4');
await tester.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
}); });

View File

@ -85,7 +85,10 @@ extension AppFlowyTestBase on WidgetTester {
bool warnIfMissed = true, bool warnIfMissed = true,
int milliseconds = 500, int milliseconds = 500,
}) async { }) async {
await tap(finder); await tap(
finder,
warnIfMissed: warnIfMissed,
);
await pumpAndSettle(Duration(milliseconds: milliseconds)); await pumpAndSettle(Duration(milliseconds: milliseconds));
return; return;
} }

View File

@ -6,6 +6,9 @@ import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart'; import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart'; import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart';
@ -555,6 +558,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(button); await tapButton(button);
} }
Future<void> scrollOptionFilterListByOffset(Offset offset) async {
await drag(find.byType(SelectOptionFilterEditor), offset);
await pumpAndSettle();
}
Future<void> enterTextInTextFilter(String text) async { Future<void> enterTextInTextFilter(String text) async {
final findEditor = find.byType(TextFilterEditor); final findEditor = find.byType(TextFilterEditor);
final findTextField = find.descendant( final findTextField = find.descendant(
@ -566,18 +574,17 @@ extension AppFlowyDatabaseTest on WidgetTester {
await pumpAndSettle(const Duration(milliseconds: 300)); await pumpAndSettle(const Duration(milliseconds: 300));
} }
Future<void> tapTextFilterDisclosureButtonInGrid() async { Future<void> tapDisclosureButtonInFinder(Finder finder) async {
final findEditor = find.byType(TextFilterEditor);
final findDisclosure = find.descendant( final findDisclosure = find.descendant(
of: findEditor, of: finder,
matching: find.byType(DisclosureButton), matching: find.byType(DisclosureButton),
); );
await tapButton(findDisclosure); await tapButton(findDisclosure);
} }
/// must call [tapTextFilterDisclosureButtonInGrid] first. /// must call [tapDisclosureButtonInFinder] first.
Future<void> tapDeleteTextFilterButtonInGrid() async { Future<void> tapDeleteFilterButtonInGrid() async {
await tapButton(find.text(LocaleKeys.grid_settings_deleteFilter.tr())); await tapButton(find.text(LocaleKeys.grid_settings_deleteFilter.tr()));
} }
@ -585,6 +592,25 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(find.byType(CheckboxFilterConditionList)); await tapButton(find.byType(CheckboxFilterConditionList));
} }
Future<void> tapChecklistFilterButtonInGrid() async {
await tapButton(find.byType(ChecklistFilterConditionList));
}
/// The [SelectOptionFilterList] must show up first.
Future<void> tapOptionFilterWithName(String name) async {
final findCell = find.descendant(
of: find.byType(SelectOptionFilterList),
matching: find.byWidgetPredicate(
(widget) =>
widget is SelectOptionFilterCell && widget.option.name == name,
skipOffstage: false,
),
skipOffstage: false,
);
expect(findCell, findsOneWidget);
await tapButton(findCell, warnIfMissed: false);
}
Future<void> tapCheckedButtonOnCheckboxFilter() async { Future<void> tapCheckedButtonOnCheckboxFilter() async {
final button = find.descendant( final button = find.descendant(
of: find.byType(HoverButton), of: find.byType(HoverButton),
@ -603,6 +629,24 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(button); await tapButton(button);
} }
Future<void> tapCompletedButtonOnChecklistFilter() async {
final button = find.descendant(
of: find.byType(HoverButton),
matching: find.text(LocaleKeys.grid_checklistFilter_isComplete.tr()),
);
await tapButton(button);
}
Future<void> tapUnCompletedButtonOnChecklistFilter() async {
final button = find.descendant(
of: find.byType(HoverButton),
matching: find.text(LocaleKeys.grid_checklistFilter_isIncomplted.tr()),
);
await tapButton(button);
}
/// Should call [tapDatabaseSettingButton] first. /// Should call [tapDatabaseSettingButton] first.
Future<void> tapDatabaseLayoutButton() async { Future<void> tapDatabaseLayoutButton() async {
final findSettingItem = find.byType(DatabaseSettingItem); final findSettingItem = find.byType(DatabaseSettingItem);

View File

@ -3,6 +3,7 @@ import 'package:appflowy/plugins/database_view/application/layout/calendar_setti
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart'; import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
@ -129,29 +130,33 @@ class DatabaseController {
Future<Either<Unit, FlowyError>> open() async { Future<Either<Unit, FlowyError>> open() async {
return _databaseViewBackendSvc.openGrid().then((result) { return _databaseViewBackendSvc.openGrid().then((result) {
return result.fold( return result.fold(
(database) async { (DatabasePB database) async {
databaseLayout = database.layoutType; databaseLayout = database.layoutType;
// Listen on layout changed if database layout is calendar
if (databaseLayout == DatabaseLayoutPB.Calendar) { if (databaseLayout == DatabaseLayoutPB.Calendar) {
_listenOnCalendarLayoutChanged(); _listenOnCalendarLayoutChanged();
} }
// Load the actual database field data.
final fieldsOrFail = await fieldController.loadFields(
fieldIds: database.fields,
);
return fieldsOrFail.fold(
(fields) {
// Notify the database is changed after the fields are loaded.
// The database won't can't be used until the fields are loaded.
_databaseCallbacks?.onDatabaseChanged?.call(database); _databaseCallbacks?.onDatabaseChanged?.call(database);
_viewCache.rowCache.setInitialRows(database.rows); _viewCache.rowCache.setInitialRows(database.rows);
return await fieldController return Future(() async {
.loadFields(
fieldIds: database.fields,
)
.then(
(result) {
return result.fold(
(l) => Future(() async {
await _loadGroups(); await _loadGroups();
await _loadLayoutSetting(); await _loadLayoutSetting();
return left(l); return left(fields);
}), });
(err) => right(err), },
); (err) {
Log.error(err);
return right(err);
}, },
); );
}, },

View File

@ -33,7 +33,8 @@ class _GridFieldNotifier extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
List<FieldInfo> get fieldInfos => _fieldInfos; UnmodifiableListView<FieldInfo> get fieldInfos =>
UnmodifiableListView(_fieldInfos);
} }
class _GridFilterNotifier extends ChangeNotifier { class _GridFilterNotifier extends ChangeNotifier {
@ -85,9 +86,11 @@ class FieldController {
final FilterBackendService _filterBackendSvc; final FilterBackendService _filterBackendSvc;
final SortBackendService _sortBackendSvc; final SortBackendService _sortBackendSvc;
bool _isDisposed = false;
// Field callbacks // Field callbacks
final Map<OnReceiveFields, VoidCallback> _fieldCallbacks = {}; final Map<OnReceiveFields, VoidCallback> _fieldCallbacks = {};
_GridFieldNotifier? _fieldNotifier = _GridFieldNotifier(); final _GridFieldNotifier _fieldNotifier = _GridFieldNotifier();
// Field updated callbacks // Field updated callbacks
final Map<OnReceiveUpdateFields, void Function(List<FieldInfo>)> final Map<OnReceiveUpdateFields, void Function(List<FieldInfo>)>
@ -107,15 +110,15 @@ class FieldController {
final Map<String, SortPB> _sortPBByFieldId = {}; final Map<String, SortPB> _sortPBByFieldId = {};
// Getters // Getters
List<FieldInfo> get fieldInfos => [..._fieldNotifier?.fieldInfos ?? []]; List<FieldInfo> get fieldInfos => [..._fieldNotifier.fieldInfos];
List<FilterInfo> get filterInfos => [..._filterNotifier?.filters ?? []]; List<FilterInfo> get filterInfos => [..._filterNotifier?.filters ?? []];
List<SortInfo> get sortInfos => [..._sortNotifier?.sorts ?? []]; List<SortInfo> get sortInfos => [..._sortNotifier?.sorts ?? []];
FieldInfo? getField(String fieldId) { FieldInfo? getField(String fieldId) {
final fields = _fieldNotifier?.fieldInfos final fields = _fieldNotifier.fieldInfos
.where((element) => element.id == fieldId) .where((element) => element.id == fieldId)
.toList() ?? .toList();
[];
if (fields.isEmpty) { if (fields.isEmpty) {
return null; return null;
} }
@ -169,6 +172,10 @@ class FieldController {
_listenOnSortChanged(); _listenOnSortChanged();
_settingBackendSvc.getSetting().then((result) { _settingBackendSvc.getSetting().then((result) {
if (_isDisposed) {
return;
}
result.fold( result.fold(
(setting) => _updateSetting(setting), (setting) => _updateSetting(setting),
(err) => Log.error(err), (err) => Log.error(err),
@ -257,6 +264,10 @@ class FieldController {
_filtersListener.start( _filtersListener.start(
onFilterChanged: (result) { onFilterChanged: (result) {
if (_isDisposed) {
return;
}
result.fold( result.fold(
(FilterChangesetNotificationPB changeset) { (FilterChangesetNotificationPB changeset) {
final List<FilterInfo> filters = filterInfos; final List<FilterInfo> filters = filterInfos;
@ -351,6 +362,9 @@ class FieldController {
_sortsListener.start( _sortsListener.start(
onSortChanged: (result) { onSortChanged: (result) {
if (_isDisposed) {
return;
}
result.fold( result.fold(
(SortChangesetNotificationPB changeset) { (SortChangesetNotificationPB changeset) {
final List<SortInfo> newSortInfos = sortInfos; final List<SortInfo> newSortInfos = sortInfos;
@ -371,6 +385,10 @@ class FieldController {
//Listen on setting changes //Listen on setting changes
_settingListener.start( _settingListener.start(
onSettingUpdated: (result) { onSettingUpdated: (result) {
if (_isDisposed) {
return;
}
result.fold( result.fold(
(setting) => _updateSetting(setting), (setting) => _updateSetting(setting),
(r) => Log.error(r), (r) => Log.error(r),
@ -385,6 +403,9 @@ class FieldController {
onFieldsChanged: (result) { onFieldsChanged: (result) {
result.fold( result.fold(
(changeset) { (changeset) {
if (_isDisposed) {
return;
}
_deleteFields(changeset.deletedFields); _deleteFields(changeset.deletedFields);
_insertFields(changeset.insertedFields); _insertFields(changeset.insertedFields);
@ -417,27 +438,29 @@ class FieldController {
} }
void _updateFieldInfos() { void _updateFieldInfos() {
if (_fieldNotifier != null) { for (final field in _fieldNotifier.fieldInfos) {
for (final field in _fieldNotifier!.fieldInfos) {
field._isGroupField = _groupConfigurationByFieldId[field.id] != null; field._isGroupField = _groupConfigurationByFieldId[field.id] != null;
field._hasFilter = _filterPBByFieldId[field.id] != null; field._hasFilter = _filterPBByFieldId[field.id] != null;
field._hasSort = _sortPBByFieldId[field.id] != null; field._hasSort = _sortPBByFieldId[field.id] != null;
} }
_fieldNotifier?.notify(); _fieldNotifier.notify();
}
} }
Future<void> dispose() async { Future<void> dispose() async {
if (_isDisposed) {
Log.warn('FieldController is already disposed');
return;
}
_isDisposed = true;
await _fieldListener.stop(); await _fieldListener.stop();
await _filtersListener.stop(); await _filtersListener.stop();
await _settingListener.stop(); await _settingListener.stop();
await _sortsListener.stop(); await _sortsListener.stop();
for (final callback in _fieldCallbacks.values) { for (final callback in _fieldCallbacks.values) {
_fieldNotifier?.removeListener(callback); _fieldNotifier.removeListener(callback);
} }
_fieldNotifier?.dispose(); _fieldNotifier.dispose();
_fieldNotifier = null;
for (final callback in _filterCallbacks.values) { for (final callback in _filterCallbacks.values) {
_filterNotifier?.removeListener(callback); _filterNotifier?.removeListener(callback);
@ -460,7 +483,11 @@ class FieldController {
return Future( return Future(
() => result.fold( () => result.fold(
(newFields) { (newFields) {
_fieldNotifier?.fieldInfos = if (_isDisposed) {
return left(unit);
}
_fieldNotifier.fieldInfos =
newFields.map((field) => FieldInfo(field: field)).toList(); newFields.map((field) => FieldInfo(field: field)).toList();
_loadFilters(); _loadFilters();
_loadSorts(); _loadSorts();
@ -551,7 +578,7 @@ class FieldController {
} }
_fieldCallbacks[onReceiveFields] = callback; _fieldCallbacks[onReceiveFields] = callback;
_fieldNotifier?.addListener(callback); _fieldNotifier.addListener(callback);
} }
if (onFilters != null) { if (onFilters != null) {
@ -588,7 +615,7 @@ class FieldController {
if (onFieldsListener != null) { if (onFieldsListener != null) {
final callback = _fieldCallbacks.remove(onFieldsListener); final callback = _fieldCallbacks.remove(onFieldsListener);
if (callback != null) { if (callback != null) {
_fieldNotifier?.removeListener(callback); _fieldNotifier.removeListener(callback);
} }
} }
if (onFiltersListener != null) { if (onFiltersListener != null) {
@ -616,7 +643,7 @@ class FieldController {
}; };
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null)); newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
_fieldNotifier?.fieldInfos = newFields; _fieldNotifier.fieldInfos = newFields;
} }
void _insertFields(List<IndexFieldPB> insertedFields) { void _insertFields(List<IndexFieldPB> insertedFields) {
@ -632,7 +659,7 @@ class FieldController {
newFieldInfos.add(fieldInfo); newFieldInfos.add(fieldInfo);
} }
} }
_fieldNotifier?.fieldInfos = newFieldInfos; _fieldNotifier.fieldInfos = newFieldInfos;
} }
List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) { List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) {
@ -654,7 +681,7 @@ class FieldController {
} }
if (updatedFields.isNotEmpty) { if (updatedFields.isNotEmpty) {
_fieldNotifier?.fieldInfos = newFields; _fieldNotifier.fieldInfos = newFields;
} }
return updatedFields; return updatedFields;
} }

View File

@ -29,9 +29,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
String get viewId => databaseController.viewId; String get viewId => databaseController.viewId;
BoardBloc({required ViewPB view}) BoardBloc({required ViewPB view})
: databaseController = DatabaseController( : databaseController = DatabaseController(view: view),
view: view,
),
super(BoardState.initial(view.id)) { super(BoardState.initial(view.id)) {
boardController = AppFlowyBoardController( boardController = AppFlowyBoardController(
onMoveGroup: ( onMoveGroup: (

View File

@ -32,15 +32,13 @@ import 'widgets/shortcuts.dart';
import 'widgets/toolbar/grid_toolbar.dart'; import 'widgets/toolbar/grid_toolbar.dart';
class GridPage extends StatefulWidget { class GridPage extends StatefulWidget {
GridPage({ const GridPage({
required this.view, required this.view,
this.onDeleted, this.onDeleted,
Key? key, Key? key,
}) : databaseController = DatabaseController(view: view), }) : super(key: key);
super(key: key);
final ViewPB view; final ViewPB view;
final DatabaseController databaseController;
final VoidCallback? onDeleted; final VoidCallback? onDeleted;
@override @override
@ -48,6 +46,14 @@ class GridPage extends StatefulWidget {
} }
class _GridPageState extends State<GridPage> { class _GridPageState extends State<GridPage> {
late DatabaseController databaseController;
@override
void initState() {
super.initState();
databaseController = DatabaseController(view: widget.view);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
@ -55,19 +61,19 @@ class _GridPageState extends State<GridPage> {
BlocProvider<GridBloc>( BlocProvider<GridBloc>(
create: (context) => GridBloc( create: (context) => GridBloc(
view: widget.view, view: widget.view,
databaseController: widget.databaseController, databaseController: databaseController,
)..add(const GridEvent.initial()), )..add(const GridEvent.initial()),
), ),
BlocProvider<GridFilterMenuBloc>( BlocProvider<GridFilterMenuBloc>(
create: (context) => GridFilterMenuBloc( create: (context) => GridFilterMenuBloc(
viewId: widget.view.id, viewId: widget.view.id,
fieldController: widget.databaseController.fieldController, fieldController: databaseController.fieldController,
)..add(const GridFilterMenuEvent.initial()), )..add(const GridFilterMenuEvent.initial()),
), ),
BlocProvider<SortMenuBloc>( BlocProvider<SortMenuBloc>(
create: (context) => SortMenuBloc( create: (context) => SortMenuBloc(
viewId: widget.view.id, viewId: widget.view.id,
fieldController: widget.databaseController.fieldController, fieldController: databaseController.fieldController,
)..add(const SortMenuEvent.initial()), )..add(const SortMenuEvent.initial()),
), ),
BlocProvider<DatabaseSettingBloc>( BlocProvider<DatabaseSettingBloc>(

View File

@ -93,7 +93,7 @@ class ChecklistState extends State<ChecklistFilterEditor> {
children: [ children: [
FlowyText(state.filterInfo.fieldInfo.name), FlowyText(state.filterInfo.fieldInfo.name),
const HSpace(4), const HSpace(4),
ChecklistFilterConditionPBList( ChecklistFilterConditionList(
filterInfo: state.filterInfo, filterInfo: state.filterInfo,
), ),
const Spacer(), const Spacer(),
@ -118,9 +118,9 @@ class ChecklistState extends State<ChecklistFilterEditor> {
} }
} }
class ChecklistFilterConditionPBList extends StatelessWidget { class ChecklistFilterConditionList extends StatelessWidget {
final FilterInfo filterInfo; final FilterInfo filterInfo;
const ChecklistFilterConditionPBList({ const ChecklistFilterConditionList({
required this.filterInfo, required this.filterInfo,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);

View File

@ -2,7 +2,6 @@ import 'package:appflowy/plugins/database_view/grid/application/filter/select_op
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -58,16 +57,16 @@ class SelectOptionFilterList extends StatelessWidget {
SelectOptionFilterListState>( SelectOptionFilterListState>(
builder: (context, state) { builder: (context, state) {
return ListView.separated( return ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
controller: ScrollController(), controller: ScrollController(),
itemCount: state.visibleOptions.length, itemCount: state.visibleOptions.length,
separatorBuilder: (context, index) { separatorBuilder: (context, index) {
return VSpace(GridSize.typeOptionSeparatorHeight); return VSpace(GridSize.typeOptionSeparatorHeight);
}, },
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final option = state.visibleOptions[index]; final option = state.visibleOptions[index];
return _SelectOptionFilterCell( return SelectOptionFilterCell(
option: option.optionPB, option: option.optionPB,
isSelected: option.isSelected, isSelected: option.isSelected,
); );
@ -80,21 +79,20 @@ class SelectOptionFilterList extends StatelessWidget {
} }
} }
class _SelectOptionFilterCell extends StatefulWidget { class SelectOptionFilterCell extends StatefulWidget {
final SelectOptionPB option; final SelectOptionPB option;
final bool isSelected; final bool isSelected;
const _SelectOptionFilterCell({ const SelectOptionFilterCell({
required this.option, required this.option,
required this.isSelected, required this.isSelected,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@override @override
State<_SelectOptionFilterCell> createState() => State<SelectOptionFilterCell> createState() => _SelectOptionFilterCellState();
_SelectOptionFilterCellState();
} }
class _SelectOptionFilterCellState extends State<_SelectOptionFilterCell> { class _SelectOptionFilterCellState extends State<SelectOptionFilterCell> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(

View File

@ -45,7 +45,7 @@ if is_empty ${ret}
end end
ret = which protoc-gen-dart ret = which protoc-gen-dart
if is_empty ${ret} if is_empty ${ret}
exec cmd.exe /c dart pub global activate protoc_plugin exec cmd.exe /c dart pub global activate protoc_plugin 20.0.1
end end
ret = which protoc-gen-dart ret = which protoc-gen-dart
if is_empty ${ret} if is_empty ${ret}
@ -60,14 +60,14 @@ script_runner = "@duckscript"
[tasks.install_flutter_protobuf_compiler] [tasks.install_flutter_protobuf_compiler]
script = """ script = """
echo "Install protoc_plugin (Dart)" echo "Install protoc_plugin (Dart)"
dart pub global activate protoc_plugin dart pub global activate protoc_plugin 20.0.1
""" """
script_runner = "@shell" script_runner = "@shell"
[tasks.install_flutter_protobuf_compiler.linux] [tasks.install_flutter_protobuf_compiler.linux]
script = """ script = """
echo "Install protoc_plugin (Dart)" echo "Install protoc_plugin (Dart)"
dart pub global activate protoc_plugin dart pub global activate protoc_plugin 20.0.1
""" """
script_runner = "@shell" script_runner = "@shell"