Skip to content

Commit

Permalink
feat: improve keyboard behavior in mobile toolbar (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyj1204 authored Jul 12, 2023
1 parent d87d353 commit 87fd7ed
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 10 deletions.
85 changes: 84 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ concurrency:
env:
FLUTTER_VERSION: "3.10.1"

# 3 jobs are configured.
# The first one runs tests on desktop OSs.
# The second runs tests on iOS simulator.
# The third runs tests on Android emulator.
jobs:
tests:
desktop:
strategy:
matrix:
os: [macos-13, macos-12, macos-11, ubuntu-22.04, ubuntu-20.04, windows-2022, windows-2019]
Expand Down Expand Up @@ -50,3 +54,82 @@ jobs:
os: ${{ matrix.os }}
attempt_limit: 10
attempt_delay: 10000
ios:
strategy:
matrix:
device:
# The available simulators are listed by the "xcrun xctrace list devices" command
- "iPhone 11 Simulator (15.2)"
fail-fast: true
runs-on: macos-11
steps:
- name: "List all simulators"
run: "xcrun xctrace list devices"
- name: "Start Simulator"
# the command "xcrun simctl boot" expects a device identifier
# the assignment of the UDID variable consists of retrieving the ID of the simulator
# by extracting it from the command "xcrun xctrace list devices"
run: |
UDID=$(xcrun xctrace list devices | grep "^${{ matrix.device }}" | awk '{gsub(/[()]/,""); print $NF}')
echo $UDID
xcrun simctl boot "${UDID:?No Simulator with this name found}"
# Similar steps as the desktop job
- uses: actions/checkout@v3

- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true

- name: Run all the tests under test/mobile
run: |
flutter pub get
flutter analyze .
dart format --set-exit-if-changed .
flutter test test/mobile --coverage
- name: Upload coverage to Codecov
uses: Wandalen/wretry.action@v1.0.36
with:
action: codecov/codecov-action@v3
with: |
fail_ci_if_error: true
verbose: true
os: ${{ matrix.device }}
attempt_limit: 10
attempt_delay: 10000
android:
strategy:
matrix:
api-level:
- 30
fail-fast: true
runs-on: macos-11
steps:
# Similar steps as the desktop job
- uses: actions/checkout@v3

- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true

- name: Run all the tests under test/mobile
run: |
flutter pub get
flutter analyze .
dart format --set-exit-if-changed .
flutter test test/mobile --coverage
- name: Upload coverage to Codecov
uses: Wandalen/wretry.action@v1.0.36
with:
action: codecov/codecov-action@v3
with: |
fail_ci_if_error: true
verbose: true
os: ${{ matrix.api-level }}
attempt_limit: 10
attempt_delay: 10000
3 changes: 3 additions & 0 deletions assets/mobile/toolbar_icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions lib/src/editor/editor_component/service/keyboard_service.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

/// [AppFlowyKeyboardService] is responsible for processing shortcut keys,
Expand Down Expand Up @@ -34,4 +35,14 @@ abstract class AppFlowyKeyboardService {
bool showCursor = false,
UnfocusDisposition disposition = UnfocusDisposition.scope,
});

/// Closes the keyboard
///
/// Used in mobile
void closeKeyboard();

/// Enable the keyboard in mobile
///
/// Used in mobile
void enableKeyBoard(Selection selection);
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
@override
void enable() => focusNode.requestFocus();

@override
void closeKeyboard() {
assert(PlatformExtension.isMobile, 'only support mobile platform');
textInputService.close();
}

@override
void enableKeyBoard(Selection selection) {
assert(PlatformExtension.isMobile, 'only support mobile platform');
_attachTextInputService(selection);
}

@override
KeyEventResult onKey(RawKeyEvent event) => throw UnimplementedError();

Expand Down
51 changes: 43 additions & 8 deletions lib/src/editor/toolbar/mobile/mobile_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,50 @@ class _MobileToolbarWidgetState extends State<MobileToolbarWidget> {
editorState: widget.editorState,
selection: widget.selection,
toolbarItems: widget.toolbarItems,
itemOnPressed: (selectedItemIndex) {
closeMenu: () {
if (_showItemMenu) {
setState(() {
_showItemMenu = false;
});
}
},
itemWithMenuOnPressed: (selectedItemIndex) {
setState(() {
// If last selected item is selected again, toggle item menu
if (_selectedToolbarItemIndex == selectedItemIndex) {
_showItemMenu = !_showItemMenu;
} else {
_selectedToolbarItemIndex = selectedItemIndex;
// If not, show item menu
_showItemMenu = true;
// close keyboard when menu pop up
widget.editorState.service.keyboardService
?.closeKeyboard();
}
_selectedToolbarItemIndex = selectedItemIndex;
});
},
),
),
_CloseKeyboardBtn(widget.editorState),
const Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: VerticalDivider(),
),
_showItemMenu
? IconButton(
padding: EdgeInsets.zero,
alignment: Alignment.centerLeft,
onPressed: () {
setState(() {
_showItemMenu = false;
widget.editorState.service.keyboardService!
.enableKeyBoard(widget.selection);
});
},
icon: const AFMobileIcon(
afMobileIcons: AFMobileIcons.close,
),
)
: _QuitEditingBtn(widget.editorState),
],
),
),
Expand All @@ -153,14 +182,16 @@ class _MobileToolbarWidgetState extends State<MobileToolbarWidget> {
}
}

class _CloseKeyboardBtn extends StatelessWidget {
const _CloseKeyboardBtn(this.editorState);
class _QuitEditingBtn extends StatelessWidget {
const _QuitEditingBtn(this.editorState);

final EditorState editorState;

@override
Widget build(BuildContext context) {
return IconButton(
padding: EdgeInsets.zero,
alignment: Alignment.centerLeft,
onPressed: () {
// clear selection to close keyboard and toolbar
editorState.selectionService.updateSelection(null);
Expand All @@ -173,13 +204,15 @@ class _CloseKeyboardBtn extends StatelessWidget {
class _ToolbarItemListView extends StatelessWidget {
const _ToolbarItemListView({
Key? key,
required this.itemOnPressed,
required this.itemWithMenuOnPressed,
required this.toolbarItems,
required this.editorState,
required this.selection,
required this.closeMenu,
}) : super(key: key);

final Function(int index) itemOnPressed;
final Function(int index) itemWithMenuOnPressed;
final Function() closeMenu;
final List<MobileToolbarItem> toolbarItems;
final EditorState editorState;
final Selection selection;
Expand All @@ -194,8 +227,10 @@ class _ToolbarItemListView extends StatelessWidget {
onPressed: () {
if (toolbarItem.hasMenu) {
// open /close current item menu through its parent widget(MobileToolbarWidget)
itemOnPressed.call(index);
itemWithMenuOnPressed.call(index);
} else {
// close menu if other item's menu is still on the screen
closeMenu.call();
toolbarItems[index].actionHandler?.call(
editorState,
selection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class _BackgroundColorOptionsWidgetsState
e.colorHex,
);
});
} else {
// TODO(yijing): handle when no text is selected
}
},
isSelected: isSelected,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class _TextColorOptionsWidgetsState extends State<TextColorOptionsWidgets> {
e.colorHex,
);
});
} else {
// TODO(yijing): handle when no text is selected
}
},
isSelected: isSelected,
Expand Down
3 changes: 2 additions & 1 deletion lib/src/infra/mobile/af_mobile_icon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ enum AFMobileIcons {
numberedList('toolbar_icons/numbered_list'),
checkbox('toolbar_icons/checkbox'),
quote('toolbar_icons/quote'),
divider('toolbar_icons/divider');
divider('toolbar_icons/divider'),
close('toolbar_icons/close');

final String iconPath;
const AFMobileIcons(this.iconPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@TestOn('android || ios')

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@TestOn('android || ios')

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@TestOn('android || ios')

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@TestOn('android || ios')

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@TestOn('android || ios')

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down

0 comments on commit 87fd7ed

Please sign in to comment.