Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Last active July 12, 2021 04:43
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save slightfoot/5db1582dcc49b1d6602fc7682fc9e987 to your computer and use it in GitHub Desktop.
Save slightfoot/5db1582dcc49b1d6602fc7682fc9e987 to your computer and use it in GitHub Desktop.
DEPRECATED. (Now integrated Into Flutter!!!). Ensure Visible for Flutter. Makes sure TextField or other widgets are scrolled into view when they receive input focus. Just pass the focusNode provided to your TextField inside the builder.
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
/// Signature for a function that creates a widget with the supplied [FocusNode].
typedef Widget EnsureVisibleBuilder(BuildContext context, FocusNode focusNode);
/// A widget that ensures it is always visible inside its ancestor scrollable
/// when focused.
class EnsureVisible extends StatefulWidget {
/// Creates a containing widget around the result of the supplied builder that
/// ensures the view is always visible in its containing [Scrollable].
const EnsureVisible({
Key key,
@required this.builder,
this.alignment,
this.duration: const Duration(milliseconds: 200),
this.curve: Curves.ease,
}) : super(key: key);
/// Builder that must be supplied. Use the callback supplied [FocusNode] with
/// your own child widgets like [TextField]. Doing this will ensure that when
/// they receive the input focus they are scrolled into view.
final EnsureVisibleBuilder builder;
/// The `alignment` argument describes where the target should be positioned
/// after applying the returned offset.
///
/// The widget only uses the vertical axis value.
///
/// If `alignment` is null then the widget will determine how much to scroll
/// the [Scrollable] by to get it itself into view.
///
/// If `alignment` is 0.0, the child must
/// be positioned as close to the leading edge of the viewport as possible.
///
/// If `alignment` is 1.0, the child must be positioned as close to the trailing
/// edge of the viewport as possible.
///
/// If `alignment` is 0.5, the child must be positioned as close to the center
/// of the viewport as possible.
final Alignment alignment;
/// Duration of the scroll animation.
final Duration duration;
/// The curve to apply to the scroll animation.
final Curve curve;
@override
_EnsureVisibleState createState() => _EnsureVisibleState();
}
class _EnsureVisibleState extends State<EnsureVisible> with WidgetsBindingObserver {
final FocusNode _focusNode = new FocusNode();
bool _alreadyScrolling = false;
@override
void initState() {
super.initState();
_focusNode.addListener(didChangeMetrics);
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_focusNode.removeListener(didChangeMetrics);
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.builder(context, _focusNode);
}
@override
void didChangeMetrics() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_focusNode.hasFocus && !_alreadyScrolling) {
final alignment = resolveAlignment();
if (alignment != null) {
_alreadyScrolling = true;
Scrollable.ensureVisible(context,
alignment: alignment,
duration: widget.duration,
curve: widget.curve,
).whenComplete(() => _alreadyScrolling = false);
}
}
});
}
double resolveAlignment() {
if (widget.alignment == null) {
final RenderObject object = context.findRenderObject();
final RenderAbstractViewport viewport = RenderAbstractViewport.of(object);
if (viewport == null) {
// If we have no viewport we don't attempt to scroll.
return null;
}
ScrollableState scrollableState = Scrollable.of(context);
if (scrollableState == null) {
// If we can't find a ancestor Scrollable we don't attempt to scroll.
return null;
}
ScrollPosition position = scrollableState.position;
if (position.pixels > viewport.getOffsetToReveal(object, 0.0).offset) {
// Move down to the top of the viewport
return 0.0;
}
else if (position.pixels < viewport.getOffsetToReveal(object, 1.0).offset) {
// Move up to the bottom of the viewport
return 1.0;
}
else {
// No scrolling is necessary to reveal the child
return null;
}
}
else {
// Use supplied Alignment parameter.
return 0.5 + (0.5 * widget.alignment.y);
}
}
}
@nagasaki876
Copy link

Hey, I'm new to flutter and I've been trying to implement this to my Login Screen. I would appreciate if i could see an example of how this works.
Thanks in Advance.

@Allan-Nava
Copy link

Me too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment