From 3a9a3100fce84b3e062a2dc798abb605e5fd47ee Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sun, 5 Apr 2020 20:46:00 +0200 Subject: [PATCH] Recording selector: Use a nested navigator The selector also makes use of the new work tile widget. --- lib/selectors/recording.dart | 304 +++++++++++++++++++++-------------- 1 file changed, 180 insertions(+), 124 deletions(-) diff --git a/lib/selectors/recording.dart b/lib/selectors/recording.dart index 9eba40c..c6fd2d8 100644 --- a/lib/selectors/recording.dart +++ b/lib/selectors/recording.dart @@ -1,108 +1,173 @@ import 'package:flutter/material.dart'; +import 'package:musicus/widgets/person_text.dart'; import '../backend.dart'; import '../database.dart'; import '../editors/recording.dart'; import '../widgets/works_by_composer.dart'; +import '../widgets/work_tile.dart'; import '../widgets/performance_row.dart'; +class PersonList extends StatelessWidget { + final void Function(int personId) onSelect; + + PersonList({ + this.onSelect, + }); + + @override + Widget build(BuildContext context) { + final backend = Backend.of(context); + + return Column( + children: [ + Material( + elevation: 2.0, + child: ListTile( + title: Text('Composers'), + ), + ), + Expanded( + child: StreamBuilder>( + stream: backend.db.allPersons().watch(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data.length, + itemBuilder: (context, index) { + final person = snapshot.data[index]; + return ListTile( + title: Text('${person.lastName}, ${person.firstName}'), + onTap: () => onSelect(person.id), + ); + }, + ); + } else { + return Container(); + } + }, + ), + ), + ], + ); + } +} + +class WorkList extends StatelessWidget { + final int composerId; + final void Function(int workId) onSelect; + + WorkList({ + this.composerId, + this.onSelect, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Material( + elevation: 2.0, + child: ListTile( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + title: PersonText(composerId), + ), + ), + Expanded( + child: WorksByComposer( + personId: composerId, + onTap: (selectedWork) => onSelect(selectedWork.id), + ), + ), + ], + ); + } +} + +class RecordingList extends StatelessWidget { + final int workId; + final void Function(Recording recording) onSelect; + + RecordingList({ + this.workId, + this.onSelect, + }); + + @override + Widget build(BuildContext context) { + final backend = Backend.of(context); + return Column( + children: [ + Material( + elevation: 2.0, + child: WorkTile( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + workId: workId, + ), + ), + Expanded( + child: StreamBuilder>( + stream: backend.db.recordingsByWork(workId).watch(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data.length, + itemBuilder: (context, index) { + final recording = snapshot.data[index]; + return ListTile( + title: StreamBuilder>( + stream: backend.db + .performancesByRecording(recording.id) + .watch(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final performance in snapshot.data) + PerformanceRow(performance), + ], + ); + } else { + return Container(); + } + }, + ), + onTap: () => onSelect(recording), + ); + }, + ); + } else { + return Container(); + } + }, + ), + ), + ], + ); + } +} + class RecordingsSelector extends StatefulWidget { @override _RecordingsSelectorState createState() => _RecordingsSelectorState(); } class _RecordingsSelectorState extends State { - Person composer; - Work work; - + final nestedNavigator = GlobalKey(); + @override Widget build(BuildContext context) { - final backend = Backend.of(context); - - bool showBackButton; - String titleText; - Widget content; - - if (composer != null) { - showBackButton = true; - if (work != null) { - titleText = work.title; - content = StreamBuilder>( - stream: backend.db.recordingsByWork(work.id).watch(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return ListView.builder( - itemCount: snapshot.data.length, - itemBuilder: (context, index) { - final recording = snapshot.data[index]; - return ListTile( - title: StreamBuilder>( - stream: backend.db - .performancesByRecording(recording.id) - .watch(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (final performance in snapshot.data) - PerformanceRow(performance), - ], - ); - } else { - return Container(); - } - }, - ), - onTap: () { - Navigator.pop(context, recording); - }, - ); - }, - ); - } else { - return Container(); - } - }, - ); - } else { - titleText = '${composer.firstName} ${composer.lastName}'; - content = WorksByComposer( - personId: composer.id, - onTap: (selectedWork) { - setState(() { - work = selectedWork; - }); - }, - ); - } - } else { - showBackButton = false; - titleText = 'Composers'; - - content = StreamBuilder>( - stream: backend.db.allPersons().watch(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return ListView.builder( - itemCount: snapshot.data.length, - itemBuilder: (context, index) { - final person = snapshot.data[index]; - return ListTile( - title: Text('${person.lastName}, ${person.firstName}'), - onTap: () { - setState(() { - composer = person; - }); - }, - ); - }, - ); - } else { - return Container(); - } - }, - ); + // This exists to circumvent the nested navigator when selecting a + // recording. + void popUpperNavigator(Recording recording) { + Navigator.pop(context, recording); } return WillPopScope( @@ -114,22 +179,31 @@ class _RecordingsSelectorState extends State { ), title: Text('Select recording'), ), - body: Column( - children: [ - Material( - elevation: 2.0, - child: ListTile( - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: showBackButton ? goBack : null, - ), - title: Text(titleText), - ), - ), - Expanded( - child: content, - ), - ], + body: Navigator( + key: nestedNavigator, + onGenerateRoute: (settings) => settings.name == '/' + ? MaterialPageRoute( + builder: (context) => PersonList( + onSelect: (personId) => nestedNavigator.currentState.push( + MaterialPageRoute( + builder: (context) => WorkList( + composerId: personId, + onSelect: (workId) => + nestedNavigator.currentState.push( + MaterialPageRoute( + builder: (context) => RecordingList( + workId: workId, + onSelect: (recording) => popUpperNavigator(recording), + ), + ), + ), + ), + ), + ), + ), + ) + : null, + initialRoute: '/', ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), @@ -148,25 +222,7 @@ class _RecordingsSelectorState extends State { }, ), ), - onWillPop: () => Future.value(goBack()), + onWillPop: () async => !(await nestedNavigator.currentState.maybePop()), ); } - - bool goBack() { - if (work != null) { - setState(() { - work = null; - }); - - return false; - } else if (composer != null) { - setState(() { - composer = null; - }); - - return false; - } else { - return true; - } - } }