aves/lib/widgets/search/search_page.dart
Thibault Deckers 0dbb46d9bb const galore
2021-06-08 11:05:23 +09:00

136 lines
4.2 KiB
Dart

import 'package:aves/theme/durations.dart';
import 'package:aves/utils/debouncer.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/search/search_delegate.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
static const routeName = '/search';
final CollectionSearchDelegate delegate;
final Animation<double> animation;
const SearchPage({
required this.delegate,
required this.animation,
});
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
final Debouncer _debouncer = Debouncer(delay: Durations.searchDebounceDelay);
final FocusNode _focusNode = FocusNode();
@override
void initState() {
super.initState();
widget.delegate.queryTextController.addListener(_onQueryChanged);
widget.animation.addStatusListener(_onAnimationStatusChanged);
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
_focusNode.addListener(_onFocusChanged);
widget.delegate.focusNode = _focusNode;
}
@override
void dispose() {
super.dispose();
widget.delegate.queryTextController.removeListener(_onQueryChanged);
widget.animation.removeStatusListener(_onAnimationStatusChanged);
widget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate.focusNode = null;
_focusNode.dispose();
}
void _onAnimationStatusChanged(AnimationStatus status) {
if (status != AnimationStatus.completed) {
return;
}
widget.animation.removeStatusListener(_onAnimationStatusChanged);
_focusNode.requestFocus();
}
@override
void didUpdateWidget(covariant SearchPage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.delegate != oldWidget.delegate) {
oldWidget.delegate.queryTextController.removeListener(_onQueryChanged);
widget.delegate.queryTextController.addListener(_onQueryChanged);
oldWidget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
oldWidget.delegate.focusNode = null;
widget.delegate.focusNode = _focusNode;
}
}
void _onFocusChanged() {
if (_focusNode.hasFocus && widget.delegate.currentBody != SearchBody.suggestions) {
widget.delegate.showSuggestions(context);
}
}
void _onQueryChanged() {
_debouncer(() {
if (mounted) {
// rebuild ourselves because query changed.
setState(() {});
}
});
}
void _onSearchBodyChanged() {
setState(() {
// rebuild ourselves because search body changed.
});
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
Widget? body;
switch (widget.delegate.currentBody) {
case SearchBody.suggestions:
body = KeyedSubtree(
key: const ValueKey<SearchBody>(SearchBody.suggestions),
child: widget.delegate.buildSuggestions(context),
);
break;
case SearchBody.results:
body = KeyedSubtree(
key: const ValueKey<SearchBody>(SearchBody.results),
child: widget.delegate.buildResults(context),
);
break;
case null:
break;
}
return Scaffold(
appBar: AppBar(
backgroundColor: theme.primaryColor,
iconTheme: theme.primaryIconTheme,
textTheme: theme.primaryTextTheme,
brightness: theme.primaryColorBrightness,
leading: widget.delegate.buildLeading(context),
title: TextField(
controller: widget.delegate.queryTextController,
focusNode: _focusNode,
style: theme.textTheme.headline6,
textInputAction: TextInputAction.search,
onSubmitted: (_) => widget.delegate.showResults(context),
decoration: InputDecoration(
border: InputBorder.none,
hintText: context.l10n.searchCollectionFieldHint,
hintStyle: theme.inputDecorationTheme.hintStyle,
),
),
actions: widget.delegate.buildActions(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
);
}
}