Close keyboard when scrolling on search results. Add ScrollDecorator.onInit.
This commit is contained in:
parent
fdaa834f0c
commit
0103ff16e8
@ -4,12 +4,17 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
/// Easily add visual decorations to a scrolling widget based on the state of its controller.
|
/// Easily add visual decorations to a scrolling widget based on the state of its controller.
|
||||||
class ScrollDecorator extends StatefulWidget {
|
class ScrollDecorator extends StatefulWidget {
|
||||||
|
|
||||||
/// Creates a widget that builds foreground and/or background decorations for a scrolling widget based on the state of
|
/// Creates a widget that builds foreground and/or background decorations for a scrolling widget based on the state of
|
||||||
/// its ScrollController.
|
/// its ScrollController.
|
||||||
// ignore: prefer_const_constructors_in_immutables
|
// ignore: prefer_const_constructors_in_immutables
|
||||||
ScrollDecorator({Key? key, required this.builder, this.foregroundBuilder, this.backgroundBuilder, this.controller})
|
ScrollDecorator({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
required this.builder,
|
||||||
|
this.fgBuilder,
|
||||||
|
this.bgBuilder,
|
||||||
|
this.controller,
|
||||||
|
this.onInit,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Creates a ScrollDecorator that fades a widget in at the begin or end of the scrolling widget based on the scroll
|
/// Creates a ScrollDecorator that fades a widget in at the begin or end of the scrolling widget based on the scroll
|
||||||
/// position. For example on a vertical list, it would fade in the `begin` widget when the list is not scrolled to the
|
/// position. For example on a vertical list, it would fade in the `begin` widget when the list is not scrolled to the
|
||||||
@ -18,9 +23,10 @@ class ScrollDecorator extends StatefulWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required this.builder,
|
required this.builder,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.onInit,
|
||||||
Widget? begin,
|
Widget? begin,
|
||||||
Widget? end,
|
Widget? end,
|
||||||
bool background = false,
|
bool bg = false,
|
||||||
Axis direction = Axis.vertical,
|
Axis direction = Axis.vertical,
|
||||||
Duration duration = const Duration(milliseconds: 150),
|
Duration duration = const Duration(milliseconds: 150),
|
||||||
}) : super(key: key) {
|
}) : super(key: key) {
|
||||||
@ -45,8 +51,8 @@ class ScrollDecorator extends StatefulWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
backgroundBuilder = background ? flexBuilder : null;
|
bgBuilder = bg ? flexBuilder : null;
|
||||||
foregroundBuilder = !background ? flexBuilder : null;
|
fgBuilder = !bg ? flexBuilder : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an ScrollDecorator that adds a shadow to the top of a vertical list when it is scrolled down.
|
/// Creates an ScrollDecorator that adds a shadow to the top of a vertical list when it is scrolled down.
|
||||||
@ -54,10 +60,11 @@ class ScrollDecorator extends StatefulWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required this.builder,
|
required this.builder,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.onInit,
|
||||||
Color color = Colors.black54,
|
Color color = Colors.black54,
|
||||||
}) : super(key: key) {
|
}) : super(key: key) {
|
||||||
backgroundBuilder = null;
|
bgBuilder = null;
|
||||||
foregroundBuilder = (controller) {
|
fgBuilder = (controller) {
|
||||||
final double ratio = controller.hasClients ? min(1, controller.position.extentBefore / 60) : 0;
|
final double ratio = controller.hasClients ? min(1, controller.position.extentBefore / 60) : 0;
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -84,11 +91,13 @@ class ScrollDecorator extends StatefulWidget {
|
|||||||
|
|
||||||
/// Builder to create the decoration widget that will be layered in front of the scrolling widget. It should use the
|
/// Builder to create the decoration widget that will be layered in front of the scrolling widget. It should use the
|
||||||
/// provided ScrollController to adjust its output as appropriate.
|
/// provided ScrollController to adjust its output as appropriate.
|
||||||
late final ScrollBuilder? foregroundBuilder;
|
late final ScrollBuilder? fgBuilder;
|
||||||
|
|
||||||
/// Builder to create the decoration widget that will be layered behind the scrolling widget. It should use the
|
/// Builder to create the decoration widget that will be layered behind the scrolling widget. It should use the
|
||||||
/// provided ScrollController to adjust its output as appropriate.
|
/// provided ScrollController to adjust its output as appropriate.
|
||||||
late final ScrollBuilder? backgroundBuilder;
|
late final ScrollBuilder? bgBuilder;
|
||||||
|
|
||||||
|
final void Function(ScrollController controller)? onInit;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ScrollDecorator> createState() => _ScrollDecoratorState();
|
State<ScrollDecorator> createState() => _ScrollDecoratorState();
|
||||||
@ -98,9 +107,12 @@ class _ScrollDecoratorState extends State<ScrollDecorator> {
|
|||||||
ScrollController? _controller;
|
ScrollController? _controller;
|
||||||
late Widget content;
|
late Widget content;
|
||||||
|
|
||||||
|
ScrollController get currentController => (widget.controller ?? _controller)!;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
if (widget.controller == null) _controller = ScrollController();
|
if (widget.controller == null) _controller = ScrollController();
|
||||||
|
widget.onInit?.call(currentController);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,17 +124,19 @@ class _ScrollDecoratorState extends State<ScrollDecorator> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ScrollController controller = (widget.controller ?? _controller)!;
|
content = widget.builder(currentController);
|
||||||
|
|
||||||
content = widget.builder(controller);
|
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: controller,
|
animation: currentController,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
if (widget.backgroundBuilder != null) widget.backgroundBuilder!(controller),
|
if (widget.bgBuilder != null) ...[
|
||||||
|
widget.bgBuilder!(currentController),
|
||||||
|
],
|
||||||
content,
|
content,
|
||||||
if (widget.foregroundBuilder != null) widget.foregroundBuilder!(controller),
|
if (widget.fgBuilder != null) ...[
|
||||||
|
widget.fgBuilder!(currentController),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,32 +1,56 @@
|
|||||||
part of '../artifact_search_screen.dart';
|
part of '../artifact_search_screen.dart';
|
||||||
|
|
||||||
/// Staggered Masonry styled grid for displaying two columns of different aspect-ratio images.
|
/// Staggered Masonry styled grid for displaying two columns of different aspect-ratio images.
|
||||||
class _ResultsGrid extends StatelessWidget {
|
class _ResultsGrid extends StatefulWidget {
|
||||||
const _ResultsGrid({Key? key, required this.searchResults, required this.onPressed}) : super(key: key);
|
_ResultsGrid({Key? key, required this.searchResults, required this.onPressed}) : super(key: key);
|
||||||
final void Function(SearchData) onPressed;
|
final void Function(SearchData) onPressed;
|
||||||
final List<SearchData> searchResults;
|
final List<SearchData> searchResults;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ResultsGrid> createState() => _ResultsGridState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ResultsGridState extends State<_ResultsGrid> {
|
||||||
|
late ScrollController _controller;
|
||||||
|
|
||||||
|
double _prevVel = -1;
|
||||||
|
|
||||||
|
void _handleResultsScrolled() {
|
||||||
|
// Hide the keyboard if the list is scrolled manually by the pointer, ignoring velocity based scroll changes
|
||||||
|
// ignore: INVALID_USE_OF_PROTECTED_MEMBER, INVALID_USE_OF_VISIBLE_FOR_TESTING_MEMBER
|
||||||
|
final vel = _controller.position.activity?.velocity;
|
||||||
|
if (vel == 0 && _prevVel == 0) {
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
}
|
||||||
|
_prevVel = vel ?? _prevVel;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ScrollDecorator.shadow(
|
return ScrollDecorator.shadow(
|
||||||
builder: (controller) => CustomScrollView(
|
onInit: (controller) => controller.addListener(_handleResultsScrolled),
|
||||||
controller: controller,
|
builder: (controller) {
|
||||||
scrollBehavior: const ScrollBehavior().copyWith(scrollbars: false),
|
_controller = controller;
|
||||||
clipBehavior: Clip.hardEdge,
|
return CustomScrollView(
|
||||||
slivers: [
|
controller: controller,
|
||||||
SliverToBoxAdapter(child: _buildLanguageMessage(context)),
|
scrollBehavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||||
SliverPadding(
|
clipBehavior: Clip.hardEdge,
|
||||||
padding: EdgeInsets.all($styles.insets.sm).copyWith(bottom: $styles.insets.offset * 1.5),
|
slivers: [
|
||||||
sliver: SliverMasonryGrid.count(
|
SliverToBoxAdapter(child: _buildLanguageMessage(context)),
|
||||||
crossAxisCount: 2,
|
SliverPadding(
|
||||||
mainAxisSpacing: $styles.insets.sm,
|
padding: EdgeInsets.all($styles.insets.sm).copyWith(bottom: $styles.insets.offset * 1.5),
|
||||||
crossAxisSpacing: $styles.insets.sm,
|
sliver: SliverMasonryGrid.count(
|
||||||
childCount: searchResults.length,
|
crossAxisCount: 2,
|
||||||
itemBuilder: (context, index) => _ResultTile(onPressed: onPressed, data: searchResults[index]),
|
mainAxisSpacing: $styles.insets.sm,
|
||||||
|
crossAxisSpacing: $styles.insets.sm,
|
||||||
|
childCount: widget.searchResults.length,
|
||||||
|
itemBuilder: (context, index) =>
|
||||||
|
_ResultTile(onPressed: widget.onPressed, data: widget.searchResults[index]),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user