Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(bug): fix bug where footer not attached to keyboard in some device and update flutter_keyboard_visibility #35

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
21 changes: 21 additions & 0 deletions lib/src/data/keyboard_animation_data.dart
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class encapsulates bottomInset and animationBegin, allowing these properties to be bundled together. By encapsulating them within this class, we facilitate their use in a ValueNotifier, promoting a cleaner and more efficient way to manage and react to changes in these values.

Example:

ValueNotifier<KeyboardAnimationData> _keyboardAnimationDataNotifier = ValueNotifier(const KeyboardAnimationData());

This approach simplifies the tracking of keyboard animation data changes, enabling more responsive and straightforward UI updates.

Original file line number Diff line number Diff line change
@@ -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,
);
}
4 changes: 2 additions & 2 deletions lib/src/footer_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
112 changes: 65 additions & 47 deletions lib/src/keyboard_attachable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -84,73 +85,90 @@ class KeyboardAttachable extends StatefulWidget {
}

class _KeyboardAttachableState extends State<KeyboardAttachable>
with SingleTickerProviderStateMixin {
final KeyboardVisibilityController _keyboardVisibility =
const DefaultKeyboardVisibilityController();
with SingleTickerProviderStateMixin, WidgetsBindingObserver {
late final KeyboardVisibilityController _keyboardVisibility;

late final KeyboardAnimationController _controller =
KeyboardAnimationInjector(this).getPlatformController();
late StreamSubscription<bool> _visibilitySubscription;
late final KeyboardAnimationController _controller;
late final StreamSubscription<bool> _visibilitySubscription;

final _keyboardAttachableKey = GlobalKey();
double _bottomInset = 0;
double _animationBegin = 0;
late final GlobalKey _keyboardAttachableKey;
late final ValueNotifier<KeyboardAnimationData>
_keyboardAnimationDataNotifier;

@override
void initState() {
_visibilitySubscription = _keyboardVisibility.onChange.listen(_animate);
super.initState();
}

@override
Widget build(BuildContext context) {
_updateBottomSizeIfNeeded(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: <Widget>[
if (child != null)
widget.transitionBuilder(child, offsetAnimation, _bottomInset),
SizeTransition(
sizeFactor: offsetAnimation,
child: Container(
height: _bottomInset,
color: widget.backgroundColor,
),
),
],
_keyboardAttachableKey = GlobalKey();
_keyboardVisibility = const DefaultKeyboardVisibilityController();
_controller = KeyboardAnimationInjector(this).getPlatformController();
_keyboardAnimationDataNotifier = ValueNotifier(
const KeyboardAnimationData(),
);
_visibilitySubscription = _keyboardVisibility.onChange.listen(_animate);
WidgetsBinding.instance.addObserver(this);
}

@override
void dispose() {
_controller.dispose();
_visibilitySubscription.cancel();
super.dispose();
}
Widget build(BuildContext context) =>
ValueListenableBuilder<KeyboardAnimationData>(
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: <Widget>[
if (child != null)
widget.transitionBuilder(
child,
offsetAnimation,
keyboardAnimatinoData.bottomInset,
),
SizeTransition(
sizeFactor: offsetAnimation,
child: Container(
height: keyboardAnimatinoData.bottomInset,
color: widget.backgroundColor,
),
),
],
);
});

void _updateBottomSizeIfNeeded(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
@override
void didChangeMetrics() {
final mediaQuery = MediaQueryData.fromView(View.of(context));
final keyboardHeight = mediaQuery.viewInsets.bottom;
final screenHeight = mediaQuery.size.height;
final keyboardAttachableBounds = _globalBounds(key: _keyboardAttachableKey);
final bottomOffset = screenHeight - keyboardAttachableBounds.bottom;
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,
);
}
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_controller.dispose();
_keyboardAnimationDataNotifier.dispose();
_visibilitySubscription.cancel();
super.dispose();
}

void _animate(bool isKeyboardVisible) =>
isKeyboardVisible ? _controller.forward() : _controller.reverse();

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down