album picker: added filter field
This commit is contained in:
parent
3fb3cf1f88
commit
318010b66c
10 changed files with 185 additions and 51 deletions
|
@ -6,7 +6,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
// TODO TLAD upgrade AGP to 4+ when this is fixed: https://github.com/flutter/flutter/issues/58247
|
// TODO TLAD upgrade AGP to 4+ when this lands on stable: https://github.com/flutter/flutter/pull/70808
|
||||||
classpath 'com.android.tools.build:gradle:3.6.4'
|
classpath 'com.android.tools.build:gradle:3.6.4'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'com.google.gms:google-services:4.3.4'
|
classpath 'com.google.gms:google-services:4.3.4'
|
||||||
|
|
|
@ -45,7 +45,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
|
||||||
sortFactor: EntrySortFactor.date,
|
sortFactor: EntrySortFactor.date,
|
||||||
).sortedEntries;
|
).sortedEntries;
|
||||||
|
|
||||||
ValueNotifier<SourceState> stateNotifier = ValueNotifier<SourceState>(SourceState.ready);
|
ValueNotifier<SourceState> stateNotifier = ValueNotifier(SourceState.ready);
|
||||||
|
|
||||||
List<DateMetadata> _savedDates;
|
List<DateMetadata> _savedDates;
|
||||||
|
|
||||||
|
|
16
lib/utils/debouncer.dart
Normal file
16
lib/utils/debouncer.dart
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class Debouncer {
|
||||||
|
final Duration delay;
|
||||||
|
|
||||||
|
Timer _timer;
|
||||||
|
|
||||||
|
Debouncer({@required this.delay});
|
||||||
|
|
||||||
|
void call(Function action) {
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = Timer(delay, action);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,10 @@ class Durations {
|
||||||
static const staggeredAnimation = Duration(milliseconds: 375);
|
static const staggeredAnimation = Duration(milliseconds: 375);
|
||||||
static const dialogFieldReachAnimation = Duration(milliseconds: 300);
|
static const dialogFieldReachAnimation = Duration(milliseconds: 300);
|
||||||
|
|
||||||
// collection animations
|
|
||||||
static const appBarTitleAnimation = Duration(milliseconds: 300);
|
static const appBarTitleAnimation = Duration(milliseconds: 300);
|
||||||
|
static const appBarActionChangeAnimation = Duration(milliseconds: 200);
|
||||||
|
|
||||||
|
// collection animations
|
||||||
static const filterBarRemovalAnimation = Duration(milliseconds: 400);
|
static const filterBarRemovalAnimation = Duration(milliseconds: 400);
|
||||||
static const collectionOpOverlayAnimation = Duration(milliseconds: 300);
|
static const collectionOpOverlayAnimation = Duration(milliseconds: 300);
|
||||||
static const collectionScalingBackgroundAnimation = Duration(milliseconds: 200);
|
static const collectionScalingBackgroundAnimation = Duration(milliseconds: 200);
|
||||||
|
@ -40,4 +42,5 @@ class Durations {
|
||||||
static Duration staggeredAnimationDelay = Durations.staggeredAnimation ~/ 6 * timeDilation;
|
static Duration staggeredAnimationDelay = Durations.staggeredAnimation ~/ 6 * timeDilation;
|
||||||
static const doubleBackTimerDelay = Duration(milliseconds: 1000);
|
static const doubleBackTimerDelay = Duration(milliseconds: 1000);
|
||||||
static const softKeyboardDisplayDelay = Duration(milliseconds: 300);
|
static const softKeyboardDisplayDelay = Duration(milliseconds: 300);
|
||||||
|
static const searchDebounceDelay = Duration(milliseconds: 200);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,14 @@ import 'package:aves/services/android_app_service.dart';
|
||||||
import 'package:aves/services/image_file_service.dart';
|
import 'package:aves/services/image_file_service.dart';
|
||||||
import 'package:aves/widgets/collection/collection_actions.dart';
|
import 'package:aves/widgets/collection/collection_actions.dart';
|
||||||
import 'package:aves/widgets/collection/empty.dart';
|
import 'package:aves/widgets/collection/empty.dart';
|
||||||
import 'package:aves/widgets/common/action_delegates/create_album_dialog.dart';
|
|
||||||
import 'package:aves/widgets/common/action_delegates/feedback.dart';
|
import 'package:aves/widgets/common/action_delegates/feedback.dart';
|
||||||
import 'package:aves/widgets/common/action_delegates/permission_aware.dart';
|
import 'package:aves/widgets/common/action_delegates/permission_aware.dart';
|
||||||
import 'package:aves/widgets/common/action_delegates/size_aware.dart';
|
import 'package:aves/widgets/common/action_delegates/size_aware.dart';
|
||||||
import 'package:aves/widgets/common/aves_dialog.dart';
|
import 'package:aves/widgets/common/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/common/entry_actions.dart';
|
import 'package:aves/widgets/common/entry_actions.dart';
|
||||||
import 'package:aves/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
import 'package:aves/widgets/filter_grids/album_pick.dart';
|
||||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/common/chip_actions.dart';
|
|
||||||
import 'package:aves/widgets/filter_grids/common/chip_set_action_delegate.dart';
|
import 'package:aves/widgets/filter_grids/common/chip_set_action_delegate.dart';
|
||||||
import 'package:aves/widgets/filter_grids/common/filter_grid_page.dart';
|
import 'package:aves/widgets/filter_grids/common/filter_grid_page.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -70,47 +69,34 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _moveSelection(BuildContext context, {@required bool copy}) async {
|
Future<void> _moveSelection(BuildContext context, {@required bool copy}) async {
|
||||||
|
final filterNotifier = ValueNotifier('');
|
||||||
final chipSetActionDelegate = AlbumChipSetActionDelegate(source: source);
|
final chipSetActionDelegate = AlbumChipSetActionDelegate(source: source);
|
||||||
final destinationAlbum = await Navigator.push(
|
final destinationAlbum = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute<String>(
|
MaterialPageRoute<String>(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
Widget appBar = AlbumPickAppBar(
|
||||||
|
copy: copy,
|
||||||
|
actionDelegate: chipSetActionDelegate,
|
||||||
|
onFilterChanged: (filter) => filterNotifier.value = filter,
|
||||||
|
);
|
||||||
|
|
||||||
return Selector<Settings, ChipSortFactor>(
|
return Selector<Settings, ChipSortFactor>(
|
||||||
selector: (context, s) => s.albumSortFactor,
|
selector: (context, s) => s.albumSortFactor,
|
||||||
builder: (context, sortFactor, child) {
|
builder: (context, sortFactor, child) {
|
||||||
return FilterGridPage(
|
return ValueListenableBuilder<String>(
|
||||||
source: source,
|
valueListenable: filterNotifier,
|
||||||
appBar: SliverAppBar(
|
builder: (context, filter, child) => FilterGridPage(
|
||||||
leading: BackButton(),
|
source: source,
|
||||||
title: Text(copy ? 'Copy to Album' : 'Move to Album'),
|
appBar: appBar,
|
||||||
actions: [
|
filterEntries: AlbumListPage.getAlbumEntries(source, filter: filter),
|
||||||
IconButton(
|
filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)),
|
||||||
icon: Icon(AIcons.createAlbum),
|
emptyBuilder: () => EmptyContent(
|
||||||
onPressed: () async {
|
icon: AIcons.album,
|
||||||
final newAlbum = await showDialog<String>(
|
text: 'No albums',
|
||||||
context: context,
|
),
|
||||||
builder: (context) => CreateAlbumDialog(),
|
onTap: (filter) => Navigator.pop<String>(context, (filter as AlbumFilter)?.album),
|
||||||
);
|
|
||||||
if (newAlbum != null && newAlbum.isNotEmpty) {
|
|
||||||
Navigator.pop<String>(context, newAlbum);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Create album',
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(AIcons.sort),
|
|
||||||
onPressed: () => chipSetActionDelegate.onActionSelected(context, ChipSetAction.sort),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
floating: true,
|
|
||||||
),
|
),
|
||||||
filterEntries: AlbumListPage.getAlbumEntries(source),
|
|
||||||
filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)),
|
|
||||||
emptyBuilder: () => EmptyContent(
|
|
||||||
icon: AIcons.album,
|
|
||||||
text: 'No albums',
|
|
||||||
),
|
|
||||||
onTap: (filter) => Navigator.pop<String>(context, (filter as AlbumFilter)?.album),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
122
lib/widgets/filter_grids/album_pick.dart
Normal file
122
lib/widgets/filter_grids/album_pick.dart
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import 'package:aves/utils/debouncer.dart';
|
||||||
|
import 'package:aves/utils/durations.dart';
|
||||||
|
import 'package:aves/widgets/common/action_delegates/create_album_dialog.dart';
|
||||||
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
import 'package:aves/widgets/filter_grids/common/chip_actions.dart';
|
||||||
|
import 'package:aves/widgets/filter_grids/common/chip_set_action_delegate.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class AlbumPickAppBar extends StatelessWidget {
|
||||||
|
final bool copy;
|
||||||
|
final AlbumChipSetActionDelegate actionDelegate;
|
||||||
|
final ValueChanged<String> onFilterChanged;
|
||||||
|
|
||||||
|
const AlbumPickAppBar({
|
||||||
|
@required this.copy,
|
||||||
|
@required this.actionDelegate,
|
||||||
|
@required this.onFilterChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverAppBar(
|
||||||
|
leading: BackButton(),
|
||||||
|
title: Text(copy ? 'Copy to Album' : 'Move to Album'),
|
||||||
|
bottom: AlbumFilterBar(
|
||||||
|
onChanged: onFilterChanged,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(AIcons.createAlbum),
|
||||||
|
onPressed: () async {
|
||||||
|
final newAlbum = await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CreateAlbumDialog(),
|
||||||
|
);
|
||||||
|
if (newAlbum != null && newAlbum.isNotEmpty) {
|
||||||
|
Navigator.pop<String>(context, newAlbum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: 'Create album',
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(AIcons.sort),
|
||||||
|
onPressed: () => actionDelegate.onActionSelected(context, ChipSetAction.sort),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
floating: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AlbumFilterBar extends StatefulWidget implements PreferredSizeWidget {
|
||||||
|
final ValueChanged<String> onChanged;
|
||||||
|
|
||||||
|
const AlbumFilterBar({@required this.onChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size get preferredSize => Size.fromHeight(kToolbarHeight);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AlbumFilterBarState createState() => _AlbumFilterBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AlbumFilterBarState extends State<AlbumFilterBar> {
|
||||||
|
final TextEditingController _controller = TextEditingController(text: '');
|
||||||
|
final Debouncer _debouncer = Debouncer(delay: Durations.searchDebounceDelay);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final clearButton = IconButton(
|
||||||
|
icon: Icon(AIcons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
_controller.clear();
|
||||||
|
widget.onChanged('');
|
||||||
|
},
|
||||||
|
tooltip: 'Clear',
|
||||||
|
);
|
||||||
|
return Container(
|
||||||
|
height: kToolbarHeight,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Icon(AIcons.search),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
icon: Padding(
|
||||||
|
padding: EdgeInsetsDirectional.only(start: 16),
|
||||||
|
child: Icon(AIcons.search),
|
||||||
|
),
|
||||||
|
// border: OutlineInputBorder(),
|
||||||
|
hintText: MaterialLocalizations.of(context).searchFieldLabel,
|
||||||
|
hintStyle: Theme.of(context).inputDecorationTheme.hintStyle,
|
||||||
|
),
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
onChanged: (s) => _debouncer(() => widget.onChanged(s)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, child) => AnimatedSwitcher(
|
||||||
|
duration: Durations.appBarActionChangeAnimation,
|
||||||
|
transitionBuilder: (child, animation) => FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: SizeTransition(
|
||||||
|
axis: Axis.horizontal,
|
||||||
|
sizeFactor: animation,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: _controller.text.isNotEmpty ? clearButton : SizedBox(width: 16),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,12 +58,16 @@ class AlbumListPage extends StatelessWidget {
|
||||||
|
|
||||||
// common with album selection page to move/copy entries
|
// common with album selection page to move/copy entries
|
||||||
|
|
||||||
static Map<String, ImageEntry> getAlbumEntries(CollectionSource source) {
|
static Map<String, ImageEntry> getAlbumEntries(CollectionSource source, {String filter}) {
|
||||||
final pinned = settings.pinnedFilters.whereType<AlbumFilter>().map((f) => f.album);
|
final pinned = settings.pinnedFilters.whereType<AlbumFilter>().map((f) => f.album);
|
||||||
final entriesByDate = source.sortedEntriesForFilterList;
|
final entriesByDate = source.sortedEntriesForFilterList;
|
||||||
|
|
||||||
// albums are initially sorted by name at the source level
|
// albums are initially sorted by name at the source level
|
||||||
var sortedAlbums = source.sortedAlbums;
|
var sortedAlbums = source.sortedAlbums;
|
||||||
|
if (filter != null && filter.isNotEmpty) {
|
||||||
|
filter = filter.toUpperCase();
|
||||||
|
sortedAlbums = sortedAlbums.where((album) => source.getUniqueAlbumName(album).toUpperCase().contains(filter)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.albumSortFactor == ChipSortFactor.name) {
|
if (settings.albumSortFactor == ChipSortFactor.name) {
|
||||||
final pinnedAlbums = <String>[], regularAlbums = <String>[], appAlbums = <String>[], specialAlbums = <String>[];
|
final pinnedAlbums = <String>[], regularAlbums = <String>[], appAlbums = <String>[], specialAlbums = <String>[];
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ImageView extends StatefulWidget {
|
||||||
class _ImageViewState extends State<ImageView> {
|
class _ImageViewState extends State<ImageView> {
|
||||||
final PhotoViewController _photoViewController = PhotoViewController();
|
final PhotoViewController _photoViewController = PhotoViewController();
|
||||||
final PhotoViewScaleStateController _photoViewScaleStateController = PhotoViewScaleStateController();
|
final PhotoViewScaleStateController _photoViewScaleStateController = PhotoViewScaleStateController();
|
||||||
final ValueNotifier<ViewState> _viewStateNotifier = ValueNotifier<ViewState>(ViewState.zero);
|
final ValueNotifier<ViewState> _viewStateNotifier = ValueNotifier(ViewState.zero);
|
||||||
StreamSubscription<PhotoViewControllerValue> _subscription;
|
StreamSubscription<PhotoViewControllerValue> _subscription;
|
||||||
Size _photoViewChildSize;
|
Size _photoViewChildSize;
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ class ImageSearchDelegate {
|
||||||
queryTextController.text = value;
|
queryTextController.text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ValueNotifier<SearchBody> currentBodyNotifier = ValueNotifier<SearchBody>(null);
|
final ValueNotifier<SearchBody> currentBodyNotifier = ValueNotifier(null);
|
||||||
|
|
||||||
SearchBody get currentBody => currentBodyNotifier.value;
|
SearchBody get currentBody => currentBodyNotifier.value;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:aves/utils/debouncer.dart';
|
||||||
|
import 'package:aves/utils/durations.dart';
|
||||||
import 'package:aves/widgets/search/search_delegate.dart';
|
import 'package:aves/widgets/search/search_delegate.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -18,7 +20,8 @@ class SearchPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchPageState extends State<SearchPage> {
|
class _SearchPageState extends State<SearchPage> {
|
||||||
FocusNode focusNode = FocusNode();
|
final Debouncer _debouncer = Debouncer(delay: Durations.searchDebounceDelay);
|
||||||
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -26,8 +29,8 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
widget.delegate.queryTextController.addListener(_onQueryChanged);
|
widget.delegate.queryTextController.addListener(_onQueryChanged);
|
||||||
widget.animation.addStatusListener(_onAnimationStatusChanged);
|
widget.animation.addStatusListener(_onAnimationStatusChanged);
|
||||||
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
|
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
|
||||||
focusNode.addListener(_onFocusChanged);
|
_focusNode.addListener(_onFocusChanged);
|
||||||
widget.delegate.focusNode = focusNode;
|
widget.delegate.focusNode = _focusNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -37,7 +40,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
widget.animation.removeStatusListener(_onAnimationStatusChanged);
|
widget.animation.removeStatusListener(_onAnimationStatusChanged);
|
||||||
widget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
|
widget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
|
||||||
widget.delegate.focusNode = null;
|
widget.delegate.focusNode = null;
|
||||||
focusNode.dispose();
|
_focusNode.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAnimationStatusChanged(AnimationStatus status) {
|
void _onAnimationStatusChanged(AnimationStatus status) {
|
||||||
|
@ -45,7 +48,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
widget.animation.removeStatusListener(_onAnimationStatusChanged);
|
widget.animation.removeStatusListener(_onAnimationStatusChanged);
|
||||||
focusNode.requestFocus();
|
_focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -57,20 +60,20 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
oldWidget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
|
oldWidget.delegate.currentBodyNotifier.removeListener(_onSearchBodyChanged);
|
||||||
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
|
widget.delegate.currentBodyNotifier.addListener(_onSearchBodyChanged);
|
||||||
oldWidget.delegate.focusNode = null;
|
oldWidget.delegate.focusNode = null;
|
||||||
widget.delegate.focusNode = focusNode;
|
widget.delegate.focusNode = _focusNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onFocusChanged() {
|
void _onFocusChanged() {
|
||||||
if (focusNode.hasFocus && widget.delegate.currentBody != SearchBody.suggestions) {
|
if (_focusNode.hasFocus && widget.delegate.currentBody != SearchBody.suggestions) {
|
||||||
widget.delegate.showSuggestions(context);
|
widget.delegate.showSuggestions(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onQueryChanged() {
|
void _onQueryChanged() {
|
||||||
setState(() {
|
_debouncer(() => setState(() {
|
||||||
// rebuild ourselves because query changed.
|
// rebuild ourselves because query changed.
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSearchBodyChanged() {
|
void _onSearchBodyChanged() {
|
||||||
|
@ -106,7 +109,7 @@ class _SearchPageState extends State<SearchPage> {
|
||||||
leading: widget.delegate.buildLeading(context),
|
leading: widget.delegate.buildLeading(context),
|
||||||
title: TextField(
|
title: TextField(
|
||||||
controller: widget.delegate.queryTextController,
|
controller: widget.delegate.queryTextController,
|
||||||
focusNode: focusNode,
|
focusNode: _focusNode,
|
||||||
style: theme.textTheme.headline6,
|
style: theme.textTheme.headline6,
|
||||||
textInputAction: TextInputAction.search,
|
textInputAction: TextInputAction.search,
|
||||||
onSubmitted: (_) => widget.delegate.showResults(context),
|
onSubmitted: (_) => widget.delegate.showResults(context),
|
||||||
|
|
Loading…
Reference in a new issue