search entries
This commit is contained in:
parent
3da9465b1e
commit
ac8b6176c3
6 changed files with 137 additions and 51 deletions
|
@ -1,23 +1,23 @@
|
|||
import 'package:aves/model/image_decode_service.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/metadata_storage_service.dart';
|
||||
import 'package:aves/widgets/album/thumbnail_collection.dart';
|
||||
import 'package:aves/widgets/album/all_collection_page.dart';
|
||||
import 'package:aves/widgets/common/fake_app_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
runApp(AvesApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class AvesApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Aves',
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
accentColor: Colors.amberAccent,
|
||||
accentColor: Colors.indigoAccent,
|
||||
scaffoldBackgroundColor: Colors.grey[900],
|
||||
),
|
||||
home: HomePage(),
|
||||
|
@ -34,7 +34,6 @@ class _HomePageState extends State<HomePage> {
|
|||
static const EventChannel eventChannel = EventChannel('deckers.thibault/aves/mediastore');
|
||||
|
||||
List<ImageEntry> entries = List();
|
||||
bool done = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -48,12 +47,12 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
||||
(entryMap) => setState(() => entries.add(ImageEntry.fromMap(entryMap))),
|
||||
onDone: () {
|
||||
debugPrint('mediastore stream done');
|
||||
setState(() => done = true);
|
||||
},
|
||||
onError: (error) => debugPrint('mediastore stream error=$error'),
|
||||
);
|
||||
onDone: () {
|
||||
debugPrint('mediastore stream done');
|
||||
setState(() {});
|
||||
},
|
||||
onError: (error) => debugPrint('mediastore stream error=$error'),
|
||||
);
|
||||
await ImageDecodeService.getImageEntries();
|
||||
}
|
||||
|
||||
|
@ -62,10 +61,7 @@ class _HomePageState extends State<HomePage> {
|
|||
return Scaffold(
|
||||
// fake app bar so that content is safe from status bar, even though we use a SliverAppBar
|
||||
appBar: FakeAppBar(),
|
||||
body: ThumbnailCollection(
|
||||
entries: entries,
|
||||
done: done,
|
||||
),
|
||||
body: AllCollectionPage(entries: entries),
|
||||
resizeToAvoidBottomInset: false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -69,6 +69,14 @@ class ImageEntry {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ImageEntry{uri=$uri, path=$path}';
|
||||
}
|
||||
|
||||
// TODO TLAD add xmp subjects, address, etc.
|
||||
String get searchable => title.toLowerCase();
|
||||
|
||||
bool get isGif => mimeType == MimeTypes.MIME_GIF;
|
||||
|
||||
bool get isVideo => mimeType.startsWith(MimeTypes.MIME_VIDEO);
|
||||
|
|
41
lib/widgets/album/all_collection_page.dart
Normal file
41
lib/widgets/album/all_collection_page.dart
Normal file
|
@ -0,0 +1,41 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/search_delegate.dart';
|
||||
import 'package:aves/widgets/album/thumbnail_collection.dart';
|
||||
import 'package:aves/widgets/debug_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AllCollectionPage extends StatelessWidget {
|
||||
final List<ImageEntry> entries;
|
||||
|
||||
const AllCollectionPage({Key key, this.entries}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ThumbnailCollection(
|
||||
entries: entries,
|
||||
appBar: SliverAppBar(
|
||||
title: Text('Aves - All'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.search),
|
||||
onPressed: () => showSearch(
|
||||
context: context,
|
||||
delegate: ImageSearchDelegate(entries),
|
||||
),
|
||||
),
|
||||
IconButton(icon: Icon(Icons.whatshot), onPressed: () => goToDebug(context)),
|
||||
],
|
||||
floating: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future goToDebug(BuildContext context) {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DebugPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
65
lib/widgets/album/search_delegate.dart
Normal file
65
lib/widgets/album/search_delegate.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/album/thumbnail_collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ImageSearchDelegate extends SearchDelegate<ImageEntry> {
|
||||
final List<ImageEntry> entries;
|
||||
|
||||
ImageSearchDelegate(this.entries);
|
||||
|
||||
@override
|
||||
ThemeData appBarTheme(BuildContext context) {
|
||||
return Theme.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) {
|
||||
return IconButton(
|
||||
tooltip: 'Back',
|
||||
icon: AnimatedIcon(
|
||||
icon: AnimatedIcons.menu_arrow,
|
||||
progress: transitionAnimation,
|
||||
),
|
||||
onPressed: () => close(context, null),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> buildActions(BuildContext context) {
|
||||
return [
|
||||
if (query.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: 'Clear',
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
query = '';
|
||||
showSuggestions(context);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
if (query.isEmpty) {
|
||||
showSuggestions(context);
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
final lowerQuery = query.toLowerCase();
|
||||
final matches = entries.where((entry) => entry.searchable.contains(lowerQuery)).toList();
|
||||
if (matches.isEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'No match',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
return ThumbnailCollection(entries: matches);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ import 'package:aves/utils/date_utils.dart';
|
|||
import 'package:aves/widgets/album/thumbnail.dart';
|
||||
import 'package:aves/widgets/common/draggable_scrollbar.dart';
|
||||
import 'package:aves/widgets/common/outlined_text.dart';
|
||||
import 'package:aves/widgets/debug_page.dart';
|
||||
import 'package:aves/widgets/fullscreen/image_page.dart';
|
||||
import "package:collection/collection.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -12,43 +11,29 @@ import 'package:intl/intl.dart';
|
|||
|
||||
class ThumbnailCollection extends StatelessWidget {
|
||||
final List<ImageEntry> entries;
|
||||
final bool done;
|
||||
final Map<DateTime, List<ImageEntry>> sections;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final Widget appBar;
|
||||
|
||||
ThumbnailCollection({Key key, this.entries, this.done})
|
||||
: sections = groupBy(entries, (entry) => entry.monthTaken),
|
||||
final Map<DateTime, List<ImageEntry>> _sections;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
ThumbnailCollection({Key key, this.entries, this.appBar})
|
||||
: _sections = groupBy(entries, (entry) => entry.monthTaken),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// debugPrint('$runtimeType build with sections=${sections.length}');
|
||||
if (!done) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'streamed ${entries.length} items',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
);
|
||||
}
|
||||
final bottomInsets = MediaQuery.of(context).viewInsets.bottom;
|
||||
final sectionKeys = sections.keys.toList();
|
||||
final sectionKeys = _sections.keys.toList();
|
||||
return SafeArea(
|
||||
child: DraggableScrollbar.arrows(
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
title: Text('Aves - All'),
|
||||
actions: [
|
||||
IconButton(icon: Icon(Icons.whatshot), onPressed: () => goToDebug(context)),
|
||||
],
|
||||
floating: true,
|
||||
),
|
||||
if (appBar != null) appBar,
|
||||
...sectionKeys.map((sectionKey) {
|
||||
Widget sliver = SectionSliver(
|
||||
entries: entries,
|
||||
sections: sections,
|
||||
sections: _sections,
|
||||
sectionKey: sectionKey,
|
||||
);
|
||||
if (sectionKey == sectionKeys.last) {
|
||||
|
@ -61,7 +46,7 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
}),
|
||||
],
|
||||
),
|
||||
controller: scrollController,
|
||||
controller: _scrollController,
|
||||
padding: EdgeInsets.only(bottom: bottomInsets),
|
||||
labelTextBuilder: (double offset) => Text(
|
||||
"${offset ~/ 1}",
|
||||
|
@ -70,15 +55,6 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future goToDebug(BuildContext context) {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DebugPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionSliver extends StatelessWidget {
|
||||
|
|
|
@ -13,7 +13,7 @@ import 'package:aves/main.dart';
|
|||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
await tester.pumpWidget(AvesApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
|
|
Loading…
Reference in a new issue