diff --git a/.github/recipe.yaml b/.github/recipe.yaml index ef8bb5aa7..23fdfb921 100644 --- a/.github/recipe.yaml +++ b/.github/recipe.yaml @@ -6,6 +6,7 @@ plugins: flutter_inappwebview: ["tv-9.0"] flutter_tts: ["tv-9.0"] integration_test: ["tv-9.0"] + keyboard_detection_tizen: ["tv-9.0"] messageport: ["tv-9.0"] package_info_plus: ["tv-9.0"] path_provider: ["tv-9.0"] diff --git a/README.md b/README.md index 1a3b4bb92..d7f91f6d6 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**google_sign_in_tizen**](packages/google_sign_in) | [google_sign_in](https://pub.dev/packages/google_sign_in) (1st-party) | [![pub package](https://img.shields.io/pub/v/google_sign_in_tizen.svg)](https://pub.dev/packages/google_sign_in_tizen) | No | | [**in_app_purchase_tizen**](packages/in_app_purchase) | [in_app_purchase](https://pub.dev/packages/in_app_purchase) (1st-party) | [![pub package](https://img.shields.io/pub/v/in_app_purchase_tizen.svg)](https://pub.dev/packages/in_app_purchase_tizen) | No | | [**integration_test_tizen**](packages/integration_test) | [integration_test](https://github.com/flutter/flutter/tree/main/packages/integration_test) (1st-party) | [![pub package](https://img.shields.io/pub/v/integration_test_tizen.svg)](https://pub.dev/packages/integration_test_tizen) | No | +| [**keyboard_detection_tizen**](packages/keyboard_detection_tizen) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/keyboard_detection_tizen.svg)](https://pub.dev/packages/keyboard_detection_tizen) | N/A | | [**messageport_tizen**](packages/messageport) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/messageport_tizen.svg)](https://pub.dev/packages/messageport_tizen) | N/A | | [**network_info_plus_tizen**](packages/network_info_plus) | [network_info_plus](https://pub.dev/packages/network_info_plus) (1st-party) | [![pub package](https://img.shields.io/pub/v/network_info_plus_tizen.svg)](https://pub.dev/packages/network_info_plus_tizen) | No | | [**package_info_plus_tizen**](packages/package_info_plus) | [package_info_plus](https://pub.dev/packages/package_info_plus) (1st-party) | [![pub package](https://img.shields.io/pub/v/package_info_plus_tizen.svg)](https://pub.dev/packages/package_info_plus_tizen) | No | @@ -68,6 +69,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**google_sign_in_tizen**](packages/google_sign_in) | ✔️ | ✔️ | ✔️ | | [**in_app_purchase_tizen**](packages/in_app_purchase) | ✔️ | ❌ | ❌ | Only applicable for TV | | [**integration_test_tizen**](packages/integration_test) | ✔️ | ✔️ | ✔️ | +| [**keyboard_detection_tizen**](packages/keyboard_detection_tizen) | ✔️ | ✔️ | ✔️ | | [**messageport_tizen**](packages/messageport) | ✔️ | ✔️ | ✔️ | | [**network_info_plus_tizen**](packages/network_info_plus) | ✔️ | ❌ | ✔️ | API not supported on emulator. Need plugin-prebuilt to run wifi on RPI. | | [**package_info_plus_tizen**](packages/package_info_plus) | ✔️ | ✔️ | ✔️ | diff --git a/packages/keyboard_detection_tizen/.gitignore b/packages/keyboard_detection_tizen/.gitignore new file mode 100644 index 000000000..786b65cb9 --- /dev/null +++ b/packages/keyboard_detection_tizen/.gitignore @@ -0,0 +1,26 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ diff --git a/packages/keyboard_detection_tizen/CHANGELOG.md b/packages/keyboard_detection_tizen/CHANGELOG.md new file mode 100644 index 000000000..607323422 --- /dev/null +++ b/packages/keyboard_detection_tizen/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/keyboard_detection_tizen/LICENSE b/packages/keyboard_detection_tizen/LICENSE new file mode 100644 index 000000000..4b455a8ce --- /dev/null +++ b/packages/keyboard_detection_tizen/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2026 Samsung Electronics Co., Ltd. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the copyright holder nor the names of the + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/keyboard_detection_tizen/README.md b/packages/keyboard_detection_tizen/README.md new file mode 100644 index 000000000..f0c72c0f8 --- /dev/null +++ b/packages/keyboard_detection_tizen/README.md @@ -0,0 +1,55 @@ +# keyboard_detection_tizen + +A Tizen-specific Flutter plugin that detects software keyboard (input panel) +visibility and size on Tizen devices. The public API mirrors the +[`keyboard_detection`](https://pub.dev/packages/keyboard_detection) package. + +The original `keyboard_detection` package detects keyboard visibility from +`MediaQuery.viewInsets.bottom`, which is not populated on Tizen. This plugin +listens to the `tizen/internal/inputpanel` event channel exposed by the +flutter-tizen embedder instead, and reads the keyboard geometry (height, +width, position) from the same channel. + +Requires a flutter-tizen embedder that publishes geometry alongside the +state on the `tizen/internal/inputpanel` channel. + +## Usage + +```yaml +dependencies: + keyboard_detection_tizen: ^0.1.0 +``` + +```dart +import 'package:keyboard_detection_tizen/keyboard_detection_tizen.dart'; + +final controller = KeyboardDetectionController( + onChanged: (state) => debugPrint('keyboard: $state'), +); + +@override +Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + const TextField(), + StreamBuilder( + stream: controller.stream, + builder: (_, snapshot) => Text( + 'state: ${snapshot.data ?? KeyboardState.unknown} ' + 'size: ${controller.size}', + ), + ), + ], + ), + ); +} +``` + +## Limitations + +- The Tizen input panel channel emits `show` / `hide` / `will_show` only. + `KeyboardState.hiding` is therefore never reached on Tizen. +- Geometry values are physical pixels reported by `ecore_imf`. Convert with + `MediaQueryData.devicePixelRatio` if you need logical pixels. +- Floating / split keyboards are not supported. diff --git a/packages/keyboard_detection_tizen/example/.gitignore b/packages/keyboard_detection_tizen/example/.gitignore new file mode 100644 index 000000000..7c026a2f1 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/.gitignore @@ -0,0 +1,32 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/packages/keyboard_detection_tizen/example/README.md b/packages/keyboard_detection_tizen/example/README.md new file mode 100644 index 000000000..b12a69776 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/README.md @@ -0,0 +1,7 @@ +# keyboard_detection_tizen_example + +Demonstrates how to use the `keyboard_detection_tizen` plugin. + +## Getting Started + +To run this app on your Tizen device, use [flutter-tizen](https://github.com/flutter-tizen/flutter-tizen). diff --git a/packages/keyboard_detection_tizen/example/integration_test/keyboard_detection_tizen_test.dart b/packages/keyboard_detection_tizen/example/integration_test/keyboard_detection_tizen_test.dart new file mode 100644 index 000000000..4a8ba1164 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/integration_test/keyboard_detection_tizen_test.dart @@ -0,0 +1,46 @@ +// Copyright 2026 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:keyboard_detection_tizen/keyboard_detection_tizen.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + const String channelName = 'tizen/internal/inputpanel'; + const StandardMethodCodec codec = StandardMethodCodec(); + + Future emit(WidgetTester tester, Map payload) async { + final ByteData data = codec.encodeSuccessEnvelope(payload); + await tester.binding.defaultBinaryMessenger.handlePlatformMessage( + channelName, + data, + (_) {}, + ); + } + + testWidgets('reports visible on show event', (WidgetTester tester) async { + final KeyboardDetectionController controller = + KeyboardDetectionController(); + await emit(tester, {'state': 'show'}); + await tester.pump(); + expect(controller.state, KeyboardState.visible); + expect(controller.stateAsBool(), isTrue); + await controller.dispose(); + }); + + testWidgets('reports hidden on hide event', (WidgetTester tester) async { + final KeyboardDetectionController controller = + KeyboardDetectionController(); + await emit(tester, {'state': 'show'}); + await tester.pump(); + await emit(tester, {'state': 'hide'}); + await tester.pump(); + expect(controller.state, KeyboardState.hidden); + expect(controller.stateAsBool(), isFalse); + await controller.dispose(); + }); +} diff --git a/packages/keyboard_detection_tizen/example/lib/main.dart b/packages/keyboard_detection_tizen/example/lib/main.dart new file mode 100644 index 000000000..774a5c0a6 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/lib/main.dart @@ -0,0 +1,110 @@ +// Copyright 2026 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:keyboard_detection_tizen/keyboard_detection_tizen.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'keyboard_detection_tizen example', + theme: ThemeData(primarySwatch: Colors.blue), + home: const HomePage(), + ); + } +} + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + late final KeyboardDetectionController _controller; + + @override + void initState() { + super.initState(); + _controller = KeyboardDetectionController( + onChanged: (KeyboardState state) { + debugPrint('keyboard_detection_tizen: $state'); + }, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Keyboard Detection')), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + StreamBuilder( + stream: _controller.stream, + initialData: _controller.state, + builder: (BuildContext _, AsyncSnapshot snap) { + final KeyboardState s = snap.data ?? KeyboardState.unknown; + return Column( + key: const Key('status'), + children: [ + Text( + 'state: ${s.name}', + key: const Key('state-text'), + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + Text( + 'visible: ${_controller.stateAsBool() ?? "unknown"}', + key: const Key('visible-text'), + ), + const SizedBox(height: 8), + Text( + 'width: ${_controller.width.toStringAsFixed(1)} ' + '/ size(height): ${_controller.size.toStringAsFixed(1)} ', + key: const Key('size-text'), + ), + const SizedBox(height: 8), + Text( + 'position: ' + '(${_controller.position.dx.toStringAsFixed(1)}, ' + '${_controller.position.dy.toStringAsFixed(1)})', + key: const Key('position-text'), + ), + ], + ); + }, + ), + const SizedBox(height: 32), + const TextField( + decoration: InputDecoration( + labelText: 'Tap here to open keyboard', + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/keyboard_detection_tizen/example/pubspec.yaml b/packages/keyboard_detection_tizen/example/pubspec.yaml new file mode 100644 index 000000000..d20c3ea35 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/pubspec.yaml @@ -0,0 +1,26 @@ +name: keyboard_detection_tizen_example +description: Demonstrates how to use the keyboard_detection_tizen plugin. +publish_to: "none" + +environment: + sdk: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + keyboard_detection_tizen: + path: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + integration_test_tizen: + path: ../../integration_test/ + +flutter: + uses-material-design: true diff --git a/packages/keyboard_detection_tizen/example/test_driver/integration_test.dart b/packages/keyboard_detection_tizen/example/test_driver/integration_test.dart new file mode 100644 index 000000000..b38629cca --- /dev/null +++ b/packages/keyboard_detection_tizen/example/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/keyboard_detection_tizen/example/tizen/.gitignore b/packages/keyboard_detection_tizen/example/tizen/.gitignore new file mode 100644 index 000000000..ac0738be2 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/tizen/.gitignore @@ -0,0 +1,12 @@ +flutter/ +.vs/ +*.user +bin/ +obj/ + +# Tizen Core CLI (tz) related files +tizen_dotnet_project.yaml +*.csproj.backup + +# Flutter-tizen dependency information file +.app.deps.json diff --git a/packages/keyboard_detection_tizen/example/tizen/App.cs b/packages/keyboard_detection_tizen/example/tizen/App.cs new file mode 100644 index 000000000..b4f1b3ee5 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/tizen/App.cs @@ -0,0 +1,20 @@ +using Tizen.Flutter.Embedding; + +namespace Runner +{ + public class App : FlutterApplication + { + protected override void OnCreate() + { + base.OnCreate(); + + GeneratedPluginRegistrant.RegisterPlugins(this); + } + + static void Main(string[] args) + { + var app = new App(); + app.Run(args); + } + } +} diff --git a/packages/keyboard_detection_tizen/example/tizen/Runner.csproj b/packages/keyboard_detection_tizen/example/tizen/Runner.csproj new file mode 100644 index 000000000..8c5646474 --- /dev/null +++ b/packages/keyboard_detection_tizen/example/tizen/Runner.csproj @@ -0,0 +1,19 @@ + + + + Exe + tizen80 + + + + + + + + + + %(RecursiveDir) + + + + diff --git a/packages/keyboard_detection_tizen/example/tizen/shared/res/ic_launcher.png b/packages/keyboard_detection_tizen/example/tizen/shared/res/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/packages/keyboard_detection_tizen/example/tizen/shared/res/ic_launcher.png differ diff --git a/packages/keyboard_detection_tizen/example/tizen/tizen-manifest.xml b/packages/keyboard_detection_tizen/example/tizen/tizen-manifest.xml new file mode 100644 index 000000000..e669278ea --- /dev/null +++ b/packages/keyboard_detection_tizen/example/tizen/tizen-manifest.xml @@ -0,0 +1,10 @@ + + + + + + ic_launcher.png + + + + diff --git a/packages/keyboard_detection_tizen/lib/keyboard_detection_tizen.dart b/packages/keyboard_detection_tizen/lib/keyboard_detection_tizen.dart new file mode 100644 index 000000000..315db665e --- /dev/null +++ b/packages/keyboard_detection_tizen/lib/keyboard_detection_tizen.dart @@ -0,0 +1,6 @@ +// Copyright 2026 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/keyboard_detection_controller.dart' + show KeyboardDetectionCallback, KeyboardDetectionController, KeyboardState; diff --git a/packages/keyboard_detection_tizen/lib/src/keyboard_detection_controller.dart b/packages/keyboard_detection_tizen/lib/src/keyboard_detection_controller.dart new file mode 100644 index 000000000..c7274a76b --- /dev/null +++ b/packages/keyboard_detection_tizen/lib/src/keyboard_detection_controller.dart @@ -0,0 +1,209 @@ +// Copyright 2026 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +/// Possible visibility states for the software keyboard (input panel). +enum KeyboardState { + /// The state has not yet been reported by the platform. + unknown, + + /// The keyboard is fully visible. + visible, + + /// The keyboard is animating from hidden to visible. + visibling, + + /// The keyboard is fully hidden. + hidden, + + /// The keyboard is animating from visible to hidden. + /// + /// On Tizen the platform does not emit a "will hide" event, so this state is + /// not reached on Tizen devices. It is kept for API parity with the + /// `keyboard_detection` package. + hiding, +} + +/// A callback invoked when the keyboard state changes. +/// +/// Return `true` to keep receiving events, `false` to unregister automatically. +typedef KeyboardDetectionCallback = FutureOr Function( + KeyboardState state); + +/// Detects software keyboard visibility on Tizen by listening to the +/// `tizen/internal/inputpanel` event channel exposed by the flutter-tizen +/// embedder. +/// +/// Mirrors the API of the [keyboard_detection](https://pub.dev/packages/keyboard_detection) +/// package so application code can be ported to Tizen with minimal changes. +class KeyboardDetectionController { + /// Creates a controller and immediately starts listening to keyboard events. + /// + /// [onChanged] is called on every state change. Use [registerCallback] to + /// add additional listeners that may auto-unregister. + KeyboardDetectionController({this.onChanged}) { + _subscription = _channel.receiveBroadcastStream().listen( + _handleEvent, + onError: (Object _) { + // Stay in unknown state if the channel raises an error (e.g. when + // running on a host that does not provide the input panel channel). + }, + ); + } + + static const EventChannel _channel = EventChannel( + 'tizen/internal/inputpanel', + ); + + /// Called whenever the keyboard state changes. + final void Function(KeyboardState state)? onChanged; + + late final StreamSubscription _subscription; + final Map _callbacks = + {}; + final StreamController _streamController = + StreamController.broadcast(); + final Completer _sizeLoaded = Completer(); + + KeyboardState _state = KeyboardState.unknown; + double _x = 0; + double _y = 0; + double _width = 0; + double _size = 0; + + /// Broadcast stream of keyboard state changes. + Stream get stream => _streamController.stream; + + /// The latest reported keyboard state. + KeyboardState get state => _state; + + /// The last observed keyboard height in physical pixels reported by the + /// Tizen input method context. + /// + /// Returns `0` until the keyboard has been visible at least once. + double get size => _size; + + /// The last observed keyboard width in physical pixels. + double get width => _width; + + /// The last observed top-left position of the keyboard in physical pixels. + Offset get position => Offset(_x, _y); + + /// Whether [size] has been observed at least once. + bool get isSizeLoaded => _sizeLoaded.isCompleted; + + /// Resolves once [size] has a non-zero value. + Future get ensureSizeLoaded => _sizeLoaded.future; + + /// Returns the current visibility as a boolean. + /// + /// - `null` while the state is [KeyboardState.unknown]. + /// - When [includeTransitionalState] is `false` (default), the transitional + /// [KeyboardState.visibling] / [KeyboardState.hiding] states are mapped to + /// their stable counterpart so the value does not depend on prior calls. + bool? stateAsBool([bool includeTransitionalState = false]) { + return switch (_state) { + KeyboardState.unknown => null, + KeyboardState.visibling => includeTransitionalState, + KeyboardState.visible => true, + KeyboardState.hiding => !includeTransitionalState, + KeyboardState.hidden => false, + }; + } + + /// Registers a callback to be invoked on every keyboard state change. + /// + /// The callback is removed automatically the first time it returns `false`. + void registerCallback(KeyboardDetectionCallback callback) { + _callbacks[callback] = true; + } + + /// Removes a previously registered callback. + void unregisterCallback(KeyboardDetectionCallback callback) { + _callbacks.remove(callback); + } + + /// Removes all registered callbacks. + void unregisterAllCallbacks() { + _callbacks.clear(); + } + + /// Releases native subscriptions. Call when the controller is no longer + /// needed, typically from a [State.dispose]. + Future dispose() async { + await _subscription.cancel(); + await _streamController.close(); + } + + void _handleEvent(dynamic event) { + if (event is! Map) { + return; + } + final Object? state = event['state']; + if (state is! String) { + return; + } + + final double? width = _readNumber(event['width']); + final double? height = _readNumber(event['height']); + if (width != null && height != null && width > 0 && height > 0) { + _width = width; + _size = height; + _x = _readNumber(event['x']) ?? 0; + _y = _readNumber(event['y']) ?? 0; + if (!_sizeLoaded.isCompleted) { + _sizeLoaded.complete(true); + } + } else if (state == 'hide') { + _width = 0; + _size = 0; + _x = 0; + _y = 0; + } + + final KeyboardState next = switch (state) { + 'will_show' => KeyboardState.visibling, + 'show' => KeyboardState.visible, + 'will_hide' => KeyboardState.hiding, + 'hide' => KeyboardState.hidden, + _ => KeyboardState.unknown, + }; + _setState(next); + } + + static double? _readNumber(Object? value) { + if (value is num) { + return value.toDouble(); + } + return null; + } + + void _setState(KeyboardState next) { + _state = next; + if (!_streamController.isClosed) { + _streamController.add(next); + } + onChanged?.call(next); + unawaited(_executeCallbacks(next)); + } + + Future _executeCallbacks(KeyboardState state) async { + final List targets = _callbacks.keys.toList(); + for (final KeyboardDetectionCallback callback in targets) { + if (!_callbacks.containsKey(callback)) { + continue; + } + final bool keep = await callback(state); + if (!_callbacks.containsKey(callback)) { + continue; + } + if (!keep) { + _callbacks.remove(callback); + } + } + } +} diff --git a/packages/keyboard_detection_tizen/pubspec.yaml b/packages/keyboard_detection_tizen/pubspec.yaml new file mode 100644 index 000000000..0aeab3a72 --- /dev/null +++ b/packages/keyboard_detection_tizen/pubspec.yaml @@ -0,0 +1,13 @@ +name: keyboard_detection_tizen +description: A Tizen-specific Flutter plugin that detects software keyboard (input panel) visibility and state changes. +homepage: https://github.com/flutter-tizen/plugins +repository: https://github.com/flutter-tizen/plugins/tree/master/packages/keyboard_detection_tizen +version: 0.1.0 + +environment: + sdk: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter