✨ Keyboard navigation basis
This commit is contained in:
@@ -28,6 +28,7 @@ import 'package:relative_time/relative_time.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
|
import 'package:island/widgets/keyboard_navigation.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
|
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
|
||||||
|
|
||||||
@@ -244,7 +245,8 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
|
|
||||||
final router = ref.watch(routerProvider);
|
final router = ref.watch(routerProvider);
|
||||||
|
|
||||||
return MaterialApp.router(
|
return KeyboardNavigation(
|
||||||
|
child: MaterialApp.router(
|
||||||
theme: theme?.light,
|
theme: theme?.light,
|
||||||
darkTheme: theme?.dark,
|
darkTheme: theme?.dark,
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
@@ -268,6 +270,7 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
lib/widgets/keyboard_navigation.dart
Normal file
86
lib/widgets/keyboard_navigation.dart
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
enum VimMode { normal, insert }
|
||||||
|
|
||||||
|
class KeyboardNavigation extends StatefulWidget {
|
||||||
|
const KeyboardNavigation({super.key, required this.child});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<KeyboardNavigation> createState() => _KeyboardNavigationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _KeyboardNavigationState extends State<KeyboardNavigation> {
|
||||||
|
VimMode _mode = VimMode.normal;
|
||||||
|
final FocusScopeNode _focusScopeNode = FocusScopeNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusScopeNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
|
||||||
|
if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mode == VimMode.normal) {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.keyJ) {
|
||||||
|
node.focusInDirection(TraversalDirection.down);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.keyK) {
|
||||||
|
node.focusInDirection(TraversalDirection.up);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.keyH) {
|
||||||
|
final focusNode = FocusManager.instance.primaryFocus;
|
||||||
|
if (focusNode != null) {
|
||||||
|
final scrollable = Scrollable.of(focusNode.context!);
|
||||||
|
if (scrollable.position.axis == Axis.horizontal) {
|
||||||
|
scrollable.position.moveTo(scrollable.position.pixels - 50);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.focusInDirection(TraversalDirection.left);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.keyL) {
|
||||||
|
final focusNode = FocusManager.instance.primaryFocus;
|
||||||
|
if (focusNode != null) {
|
||||||
|
final scrollable = Scrollable.of(focusNode.context!);
|
||||||
|
if (scrollable.position.axis == Axis.horizontal) {
|
||||||
|
scrollable.position.moveTo(scrollable.position.pixels + 50);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.focusInDirection(TraversalDirection.right);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.keyI) {
|
||||||
|
setState(() {
|
||||||
|
_mode = VimMode.insert;
|
||||||
|
});
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
} else if (_mode == VimMode.insert) {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
|
setState(() {
|
||||||
|
_mode = VimMode.normal;
|
||||||
|
});
|
||||||
|
// Unfocus the current widget to prevent typing
|
||||||
|
node.unfocus();
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Focus(
|
||||||
|
focusNode: _focusScopeNode,
|
||||||
|
onKeyEvent: _handleKeyEvent,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user