From 15cd567744dfca00b9418db20852d2949cf9c405 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:05:11 +0800 Subject: [PATCH 01/13] chore(packages): update package number and version with sdk version --- pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8a05298..754b274 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,15 @@ name: keyboard_attachable description: A Flutter package to build widgets that can be attached to the soft keyboard. -version: 2.1.0 +version: 2.2.0 homepage: https://github.com/drogel/keyboard_attachable environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=3.1.3 <4.0.0" dependencies: flutter: sdk: flutter - flutter_keyboard_visibility: ^5.0.0 + flutter_keyboard_visibility: ^5.4.1 dev_dependencies: flutter_test: From dcaf0f2bf6f466962ce4fdcaeeb40c2c055214aa Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:05:27 +0800 Subject: [PATCH 02/13] chore(example): update sdk version constraints --- example/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c0c072c..eb410a2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,10 +1,10 @@ name: example description: A new Flutter project. -publish_to: 'none' +publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=3.1.3 <4.0.0" dependencies: cupertino_icons: ^1.0.2 From 8169e446cd02ba037c58d426e62a49a853875660 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:05:42 +0800 Subject: [PATCH 03/13] chore(gradle): update to gradle 7.5 --- example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..6b66533 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip From 8cb39987692af6df1b3ca36506edbba140954dbc Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:06:13 +0800 Subject: [PATCH 04/13] chore(gradle): update compile and target sdk version to 31 --- example/android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 6ca2344..86d5ab4 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -36,7 +36,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 30 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } From b147f93840d810942d39fe4934236f4725ba1c3d Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:06:30 +0800 Subject: [PATCH 05/13] chore(gradle): update gradle version and kotlin version --- example/android/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/android/build.gradle b/example/android/build.gradle index c505a86..94a38e7 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.7.10' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } From d7962c0bd98aa59be639fc68985b8e8bb72ad548 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Tue, 17 Oct 2023 13:07:01 +0800 Subject: [PATCH 06/13] chore(manifest): add exported=true for android 12 support --- example/android/app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 34dd77e..924a7b1 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ android:icon="@mipmap/ic_launcher"> Date: Tue, 17 Oct 2023 13:27:16 +0800 Subject: [PATCH 07/13] refactor(shouldrelayout): check for should relayout --- lib/src/footer_layout.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/footer_layout.dart b/lib/src/footer_layout.dart index 82f3e09..5eb9d68 100644 --- a/lib/src/footer_layout.dart +++ b/lib/src/footer_layout.dart @@ -51,5 +51,6 @@ class _FooterLayoutDelegate extends MultiChildLayoutDelegate { } @override - bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false; + bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => + oldDelegate != this; } From 2cfa7571470ff0ed8100c10d850038fe7f049b68 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 04:23:16 +0800 Subject: [PATCH 08/13] chore(packages): bump flutter_keyboard_visibility to 6.0.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 754b274..36d69f7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_keyboard_visibility: ^5.4.1 + flutter_keyboard_visibility: ^6.0.0 dev_dependencies: flutter_test: From 49cd9025cc1bfcd422d45cd4436c73c66503378a Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 05:16:24 +0800 Subject: [PATCH 09/13] refactor(widget): use widget binding observer update _bottomInset and _animationBegin using didChangeMetrics method --- lib/src/keyboard_attachable.dart | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/src/keyboard_attachable.dart b/lib/src/keyboard_attachable.dart index 263c8f8..cb6b1e8 100644 --- a/lib/src/keyboard_attachable.dart +++ b/lib/src/keyboard_attachable.dart @@ -84,7 +84,7 @@ class KeyboardAttachable extends StatefulWidget { } class _KeyboardAttachableState extends State - with SingleTickerProviderStateMixin { + with SingleTickerProviderStateMixin, WidgetsBindingObserver { final KeyboardVisibilityController _keyboardVisibility = const DefaultKeyboardVisibilityController(); @@ -98,13 +98,13 @@ class _KeyboardAttachableState extends State @override void initState() { - _visibilitySubscription = _keyboardVisibility.onChange.listen(_animate); super.initState(); + WidgetsBinding.instance.addObserver(this); + _visibilitySubscription = _keyboardVisibility.onChange.listen(_animate); } @override Widget build(BuildContext context) { - _updateBottomSizeIfNeeded(context); final animation = _controller.animation; final offsetAnimation = CurvedAnimation( parent: animation, @@ -129,13 +129,7 @@ class _KeyboardAttachableState extends State } @override - void dispose() { - _controller.dispose(); - _visibilitySubscription.cancel(); - super.dispose(); - } - - void _updateBottomSizeIfNeeded(BuildContext context) { + void didChangeMetrics() { final mediaQuery = MediaQuery.of(context); final keyboardHeight = mediaQuery.viewInsets.bottom; final screenHeight = mediaQuery.size.height; @@ -151,6 +145,14 @@ class _KeyboardAttachableState extends State } } + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + _controller.dispose(); + _visibilitySubscription.cancel(); + super.dispose(); + } + void _animate(bool isKeyboardVisible) => isKeyboardVisible ? _controller.forward() : _controller.reverse(); From f21dfab36821a8871bd897678092125f1eeaaef8 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 17:48:38 +0800 Subject: [PATCH 10/13] refactor(linter): remove unused linter rule --- analysis_options.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index b435a7b..75c56dc 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,7 +10,6 @@ linter: - always_declare_return_types - always_put_control_body_on_new_line - always_put_required_named_parameters_first - - always_require_non_null_named_parameters - annotate_overrides - avoid_annotating_with_dynamic - avoid_bool_literals_in_conditional_expressions @@ -31,8 +30,6 @@ linter: - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - - avoid_returning_null - - avoid_returning_null_for_future - avoid_returning_null_for_void - avoid_returning_this - avoid_setters_without_getters @@ -57,12 +54,10 @@ linter: - hash_and_equals - implementation_imports - invariant_booleans - - iterable_contains_unrelated_type - join_return_with_assignment - library_names - library_prefixes - lines_longer_than_80_chars - - list_remove_unrelated_type - literal_only_boolean_expressions - no_adjacent_strings_in_list - no_duplicate_case_values From 301c7a5fd841a258b79d0913e3b722b53ee115d7 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 17:49:15 +0800 Subject: [PATCH 11/13] feat(data): create keyboard animation data class --- lib/src/data/keyboard_animation_data.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/src/data/keyboard_animation_data.dart diff --git a/lib/src/data/keyboard_animation_data.dart b/lib/src/data/keyboard_animation_data.dart new file mode 100644 index 0000000..82eb177 --- /dev/null +++ b/lib/src/data/keyboard_animation_data.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +@immutable +class KeyboardAnimationData { + const KeyboardAnimationData({ + this.bottomInset = 0, + this.animationBegin = 0, + }); + + final double bottomInset; + final double animationBegin; + + KeyboardAnimationData copyWith({ + double? bottomInset, + double? animationBegin, + }) => + KeyboardAnimationData( + bottomInset: bottomInset ?? this.bottomInset, + animationBegin: animationBegin ?? this.animationBegin, + ); +} From 653a08d9b7c2518f181aa570fae76de2c8d099e5 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 17:50:02 +0800 Subject: [PATCH 12/13] refactor(widget): use super parameter builder --- lib/src/footer_layout.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/footer_layout.dart b/lib/src/footer_layout.dart index 5eb9d68..dc8b613 100644 --- a/lib/src/footer_layout.dart +++ b/lib/src/footer_layout.dart @@ -9,10 +9,10 @@ import 'package:flutter/widgets.dart'; class FooterLayout extends StatelessWidget { /// Creates a layout with its child widget above a footer widget. const FooterLayout({ - Key? key, + super.key, this.footer, this.child, - }) : super(key: key); + }); /// The widget to position at the bottom of the available space. final Widget? footer; From a34b5bb62100af016ce558920a36d18b9e205b98 Mon Sep 17 00:00:00 2001 From: Fajrian Aidil Pratama Date: Fri, 5 Apr 2024 17:51:59 +0800 Subject: [PATCH 13/13] fix(keyboard_attachable): fix issue where sometimes widget not attached use value notifier to update dynamic data of bottom inset and animation begin --- lib/src/keyboard_attachable.dart | 92 +++++++++++++++++++------------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/lib/src/keyboard_attachable.dart b/lib/src/keyboard_attachable.dart index cb6b1e8..ff51ca2 100644 --- a/lib/src/keyboard_attachable.dart +++ b/lib/src/keyboard_attachable.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:keyboard_attachable/keyboard_attachable.dart'; import 'package:keyboard_attachable/src/animation/keyboard_animation_controller.dart'; import 'package:keyboard_attachable/src/animation/keyboard_animation_injector.dart'; +import 'package:keyboard_attachable/src/data/keyboard_animation_data.dart'; import 'package:keyboard_attachable/src/visibility/default_keyboard_visibility_controller.dart'; import 'package:keyboard_attachable/src/visibility/keyboard_visibility_controller.dart'; @@ -37,11 +38,11 @@ class KeyboardAttachable extends StatefulWidget { /// Creates a widget that smoothly adds space below its child when the /// keyboard is shown or hidden. const KeyboardAttachable({ + super.key, this.child, this.transitionBuilder = KeyboardAttachable._defaultBuilder, this.backgroundColor = Colors.transparent, - Key? key, - }) : super(key: key); + }); /// The color that fills the space that is added when the keyboard appears. /// @@ -85,52 +86,62 @@ class KeyboardAttachable extends StatefulWidget { class _KeyboardAttachableState extends State with SingleTickerProviderStateMixin, WidgetsBindingObserver { - final KeyboardVisibilityController _keyboardVisibility = - const DefaultKeyboardVisibilityController(); + late final KeyboardVisibilityController _keyboardVisibility; - late final KeyboardAnimationController _controller = - KeyboardAnimationInjector(this).getPlatformController(); - late StreamSubscription _visibilitySubscription; + late final KeyboardAnimationController _controller; + late final StreamSubscription _visibilitySubscription; - final _keyboardAttachableKey = GlobalKey(); - double _bottomInset = 0; - double _animationBegin = 0; + late final GlobalKey _keyboardAttachableKey; + late final ValueNotifier + _keyboardAnimationDataNotifier; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + _keyboardAttachableKey = GlobalKey(); + _keyboardVisibility = const DefaultKeyboardVisibilityController(); + _controller = KeyboardAnimationInjector(this).getPlatformController(); + _keyboardAnimationDataNotifier = ValueNotifier( + const KeyboardAnimationData(), + ); _visibilitySubscription = _keyboardVisibility.onChange.listen(_animate); + WidgetsBinding.instance.addObserver(this); } @override - Widget build(BuildContext context) { - final animation = _controller.animation; - final offsetAnimation = CurvedAnimation( - parent: animation, - curve: Interval(_animationBegin, 1), - ); - final child = widget.child; - return Column( - key: _keyboardAttachableKey, - mainAxisSize: MainAxisSize.min, - children: [ - if (child != null) - widget.transitionBuilder(child, offsetAnimation, _bottomInset), - SizeTransition( - sizeFactor: offsetAnimation, - child: Container( - height: _bottomInset, - color: widget.backgroundColor, - ), - ), - ], - ); - } + Widget build(BuildContext context) => + ValueListenableBuilder( + valueListenable: _keyboardAnimationDataNotifier, + builder: (_, keyboardAnimatinoData, __) { + final offsetAnimation = CurvedAnimation( + parent: _controller.animation, + curve: Interval(keyboardAnimatinoData.animationBegin, 1), + ); + final child = widget.child; + return Column( + key: _keyboardAttachableKey, + mainAxisSize: MainAxisSize.min, + children: [ + if (child != null) + widget.transitionBuilder( + child, + offsetAnimation, + keyboardAnimatinoData.bottomInset, + ), + SizeTransition( + sizeFactor: offsetAnimation, + child: Container( + height: keyboardAnimatinoData.bottomInset, + color: widget.backgroundColor, + ), + ), + ], + ); + }); @override void didChangeMetrics() { - final mediaQuery = MediaQuery.of(context); + final mediaQuery = MediaQueryData.fromView(View.of(context)); final keyboardHeight = mediaQuery.viewInsets.bottom; final screenHeight = mediaQuery.size.height; final keyboardAttachableBounds = _globalBounds(key: _keyboardAttachableKey); @@ -138,10 +149,14 @@ class _KeyboardAttachableState extends State final bottomInset = (keyboardHeight - bottomOffset).clamp(0, keyboardHeight).toDouble(); final isKeyboardDismissed = keyboardHeight == 0; - final animationBegin = (1 - bottomInset / keyboardHeight).toDouble(); + final animationBegin = keyboardHeight != 0 + ? (1 - bottomInset / keyboardHeight).toDouble() + : 0.0; if (bottomInset > 0) { - _bottomInset = bottomInset; - _animationBegin = isKeyboardDismissed ? 0 : animationBegin; + _keyboardAnimationDataNotifier.value = KeyboardAnimationData( + animationBegin: isKeyboardDismissed ? 0 : animationBegin, + bottomInset: bottomInset, + ); } } @@ -149,6 +164,7 @@ class _KeyboardAttachableState extends State void dispose() { WidgetsBinding.instance.removeObserver(this); _controller.dispose(); + _keyboardAnimationDataNotifier.dispose(); _visibilitySubscription.cancel(); super.dispose(); }