mirror of
https://github.com/johrpan/musicus_mobile.git
synced 2025-10-26 18:57:25 +01:00
Rename top level directories
This commit is contained in:
parent
256bda1b72
commit
674ce19414
90 changed files with 0 additions and 0 deletions
66
mobile/lib/editors/ensemble.dart
Normal file
66
mobile/lib/editors/ensemble.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
|
||||
class EnsembleEditor extends StatefulWidget {
|
||||
final Ensemble ensemble;
|
||||
|
||||
EnsembleEditor({
|
||||
this.ensemble,
|
||||
});
|
||||
|
||||
@override
|
||||
_EnsembleEditorState createState() => _EnsembleEditorState();
|
||||
}
|
||||
|
||||
class _EnsembleEditorState extends State<EnsembleEditor> {
|
||||
final nameController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.ensemble != null) {
|
||||
nameController.text = widget.ensemble.name;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Ensemble'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final ensemble = Ensemble(
|
||||
id: widget.ensemble?.id ?? generateId(),
|
||||
name: nameController.text,
|
||||
);
|
||||
|
||||
await backend.db.updateEnsemble(ensemble);
|
||||
Navigator.pop(context, ensemble);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Name',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
66
mobile/lib/editors/instrument.dart
Normal file
66
mobile/lib/editors/instrument.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
|
||||
class InstrumentEditor extends StatefulWidget {
|
||||
final Instrument instrument;
|
||||
|
||||
InstrumentEditor({
|
||||
this.instrument,
|
||||
});
|
||||
|
||||
@override
|
||||
_InstrumentEditorState createState() => _InstrumentEditorState();
|
||||
}
|
||||
|
||||
class _InstrumentEditorState extends State<InstrumentEditor> {
|
||||
final nameController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.instrument != null) {
|
||||
nameController.text = widget.instrument.name;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Instrument/Role'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final instrument = Instrument(
|
||||
id: widget.instrument?.id ?? generateId(),
|
||||
name: nameController.text,
|
||||
);
|
||||
|
||||
await backend.db.updateInstrument(instrument);
|
||||
Navigator.pop(context, instrument);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Name',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
78
mobile/lib/editors/person.dart
Normal file
78
mobile/lib/editors/person.dart
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
|
||||
class PersonEditor extends StatefulWidget {
|
||||
final Person person;
|
||||
|
||||
PersonEditor({
|
||||
this.person,
|
||||
});
|
||||
|
||||
@override
|
||||
_PersonEditorState createState() => _PersonEditorState();
|
||||
}
|
||||
|
||||
class _PersonEditorState extends State<PersonEditor> {
|
||||
final firstNameController = TextEditingController();
|
||||
final lastNameController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.person != null) {
|
||||
firstNameController.text = widget.person.firstName;
|
||||
lastNameController.text = widget.person.lastName;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Person'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final person = Person(
|
||||
id: widget.person?.id ?? generateId(),
|
||||
firstName: firstNameController.text,
|
||||
lastName: lastNameController.text,
|
||||
);
|
||||
|
||||
await backend.db.updatePerson(person);
|
||||
Navigator.pop(context, person);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: firstNameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'First name',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: lastNameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Last name',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
140
mobile/lib/editors/recording.dart
Normal file
140
mobile/lib/editors/recording.dart
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
import '../selectors/performer.dart';
|
||||
import '../selectors/work.dart';
|
||||
import '../widgets/texts.dart';
|
||||
|
||||
class RecordingEditor extends StatefulWidget {
|
||||
final Recording recording;
|
||||
|
||||
RecordingEditor({
|
||||
this.recording,
|
||||
});
|
||||
|
||||
@override
|
||||
_RecordingEditorState createState() => _RecordingEditorState();
|
||||
}
|
||||
|
||||
class _RecordingEditorState extends State<RecordingEditor> {
|
||||
final commentController = TextEditingController();
|
||||
|
||||
Work work;
|
||||
List<PerformanceModel> performances = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.recording != null) {
|
||||
// TODO: Initialize.
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backend = Backend.of(context);
|
||||
|
||||
Future<void> selectWork() async {
|
||||
final Work newWork = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => WorkSelector(),
|
||||
fullscreenDialog: true,
|
||||
));
|
||||
|
||||
if (newWork != null) {
|
||||
setState(() {
|
||||
work = newWork;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Recording'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final recording = Recording(
|
||||
id: widget.recording?.id ?? generateId(),
|
||||
work: work.id,
|
||||
comment: commentController.text,
|
||||
);
|
||||
|
||||
await backend.db.updateRecording(recording, performances);
|
||||
Navigator.pop(context, recording);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
work != null
|
||||
? ListTile(
|
||||
title: WorkText(work.id),
|
||||
subtitle: ComposersText(work.id),
|
||||
onTap: selectWork,
|
||||
)
|
||||
: ListTile(
|
||||
title: Text('Work'),
|
||||
subtitle: Text('Select work'),
|
||||
onTap: selectWork,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 0.0,
|
||||
bottom: 16.0,
|
||||
),
|
||||
child: TextField(
|
||||
controller: commentController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Comment',
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Performers'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
final PerformanceModel model = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PerformerSelector(),
|
||||
fullscreenDialog: true,
|
||||
));
|
||||
|
||||
if (model != null) {
|
||||
setState(() {
|
||||
performances.add(model);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
for (final performance in performances)
|
||||
ListTile(
|
||||
title: Text(performance.person != null
|
||||
? '${performance.person.firstName} ${performance.person.lastName}'
|
||||
: performance.ensemble.name),
|
||||
subtitle:
|
||||
performance.role != null ? Text(performance.role.name) : null,
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
performances.remove(performance);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
158
mobile/lib/editors/tracks.dart
Normal file
158
mobile/lib/editors/tracks.dart
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
import '../music_library.dart';
|
||||
import '../selectors/files.dart';
|
||||
import '../selectors/recording.dart';
|
||||
import '../widgets/recording_tile.dart';
|
||||
|
||||
class TrackModel {
|
||||
int workPartIndex;
|
||||
String workPartTitle;
|
||||
String fileName;
|
||||
|
||||
TrackModel(this.fileName);
|
||||
}
|
||||
|
||||
class TracksEditor extends StatefulWidget {
|
||||
@override
|
||||
_TracksEditorState createState() => _TracksEditorState();
|
||||
}
|
||||
|
||||
class _TracksEditorState extends State<TracksEditor> {
|
||||
BackendState backend;
|
||||
int recordingId;
|
||||
String parentId;
|
||||
List<TrackModel> trackModels = [];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
backend = Backend.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Tracks'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final List<Track> tracks = [];
|
||||
|
||||
for (var i = 0; i < trackModels.length; i++) {
|
||||
final trackModel = trackModels[i];
|
||||
|
||||
tracks.add(Track(
|
||||
fileName: trackModel.fileName,
|
||||
recordingId: recordingId,
|
||||
index: i,
|
||||
partIds: [trackModel.workPartIndex],
|
||||
));
|
||||
}
|
||||
|
||||
backend.ml.addTracks(parentId, tracks);
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ReorderableListView(
|
||||
header: Column(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: recordingId != null
|
||||
? RecordingTile(
|
||||
recordingId: recordingId,
|
||||
)
|
||||
: Text('Select recording'),
|
||||
onTap: selectRecording,
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Files'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () async {
|
||||
final FilesSelectorResult result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FilesSelector(),
|
||||
),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
final List<TrackModel> newTrackModels = [];
|
||||
|
||||
for (final document in result.selection) {
|
||||
newTrackModels.add(TrackModel(document.name));
|
||||
}
|
||||
|
||||
setState(() {
|
||||
parentId = result.parentId;
|
||||
trackModels = newTrackModels;
|
||||
});
|
||||
|
||||
if (recordingId != null) {
|
||||
updateAutoParts();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
children: trackModels
|
||||
.map((t) => ListTile(
|
||||
key: Key(t.hashCode.toString()),
|
||||
leading: const Icon(Icons.drag_handle),
|
||||
title: Text(t.workPartTitle ?? 'Set work part'),
|
||||
subtitle: Text(t.fileName),
|
||||
))
|
||||
.toList(),
|
||||
onReorder: (i1, i2) {
|
||||
setState(() {
|
||||
final track = trackModels.removeAt(i1);
|
||||
final newIndex = i2 > i1 ? i2 - 1 : i2;
|
||||
trackModels.insert(newIndex, track);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> selectRecording() async {
|
||||
final Recording recording = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RecordingsSelector(),
|
||||
),
|
||||
);
|
||||
|
||||
if (recording != null) {
|
||||
setState(() {
|
||||
recordingId = recording.id;
|
||||
});
|
||||
|
||||
updateAutoParts();
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically associate the tracks with work parts.
|
||||
Future<void> updateAutoParts() async {
|
||||
final recording = await backend.db.recordingById(recordingId).getSingle();
|
||||
final workId = recording.work;
|
||||
final workParts = await backend.db.workParts(workId).get();
|
||||
|
||||
setState(() {
|
||||
for (var i = 0; i < trackModels.length; i++) {
|
||||
if (i >= workParts.length) {
|
||||
trackModels[i].workPartIndex = null;
|
||||
trackModels[i].workPartTitle = null;
|
||||
} else {
|
||||
trackModels[i].workPartIndex = workParts[i].partIndex;
|
||||
trackModels[i].workPartTitle = workParts[i].title;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
378
mobile/lib/editors/work.dart
Normal file
378
mobile/lib/editors/work.dart
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../backend.dart';
|
||||
import '../database.dart';
|
||||
import '../selectors/instruments.dart';
|
||||
import '../selectors/person.dart';
|
||||
|
||||
class PartData {
|
||||
final titleController = TextEditingController();
|
||||
|
||||
Person composer;
|
||||
List<Instrument> instruments;
|
||||
|
||||
PartData({
|
||||
String title,
|
||||
this.composer,
|
||||
this.instruments = const [],
|
||||
}) {
|
||||
titleController.text = title ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
class WorkProperties extends StatelessWidget {
|
||||
final TextEditingController titleController;
|
||||
final Person composer;
|
||||
final List<Instrument> instruments;
|
||||
final void Function(Person) onComposerChanged;
|
||||
final void Function(List<Instrument>) onInstrumentsChanged;
|
||||
|
||||
WorkProperties({
|
||||
@required this.titleController,
|
||||
@required this.composer,
|
||||
@required this.instruments,
|
||||
@required this.onComposerChanged,
|
||||
@required this.onInstrumentsChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: titleController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Title',
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Composer'),
|
||||
subtitle: Text(composer != null
|
||||
? '${composer.firstName} ${composer.lastName}'
|
||||
: 'Select composer'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
onComposerChanged(null);
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
final Person person = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PersonsSelector(),
|
||||
fullscreenDialog: true,
|
||||
));
|
||||
|
||||
if (person != null) {
|
||||
onComposerChanged(person);
|
||||
}
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Instruments'),
|
||||
subtitle: Text(instruments.isNotEmpty
|
||||
? instruments.map((i) => i.name).join(', ')
|
||||
: 'Select instruments'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
onInstrumentsChanged([]);
|
||||
}),
|
||||
onTap: () async {
|
||||
final List<Instrument> selection = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => InstrumentsSelector(
|
||||
multiple: true,
|
||||
selection: instruments,
|
||||
),
|
||||
fullscreenDialog: true,
|
||||
));
|
||||
|
||||
if (selection != null) {
|
||||
onInstrumentsChanged(selection);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PartTile extends StatefulWidget {
|
||||
final PartData part;
|
||||
final void Function() onMore;
|
||||
final void Function() onDelete;
|
||||
|
||||
PartTile({
|
||||
Key key,
|
||||
@required this.part,
|
||||
@required this.onMore,
|
||||
@required this.onDelete,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PartTileState createState() => _PartTileState();
|
||||
}
|
||||
|
||||
class _PartTileState extends State<PartTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 8.0),
|
||||
child: Icon(
|
||||
Icons.drag_handle,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: widget.part.titleController,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: 'Part title',
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: widget.onMore,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: widget.onDelete,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WorkEditor extends StatefulWidget {
|
||||
final Work work;
|
||||
|
||||
WorkEditor({
|
||||
this.work,
|
||||
});
|
||||
|
||||
@override
|
||||
_WorkEditorState createState() => _WorkEditorState();
|
||||
}
|
||||
|
||||
class _WorkEditorState extends State<WorkEditor> {
|
||||
final titleController = TextEditingController();
|
||||
|
||||
BackendState backend;
|
||||
|
||||
String title = '';
|
||||
Person composer;
|
||||
List<Instrument> instruments = [];
|
||||
List<PartData> parts = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.work != null) {
|
||||
titleController.text = widget.work.title;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
backend = Backend.of(context);
|
||||
|
||||
if (widget.work != null) {
|
||||
if (widget.work.composer != null) {
|
||||
() async {
|
||||
final person =
|
||||
await backend.db.personById(widget.work.composer).getSingle();
|
||||
|
||||
// We don't want to override a newly selected composer.
|
||||
if (composer == null) {
|
||||
setState(() {
|
||||
composer = person;
|
||||
});
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
() async {
|
||||
final selection =
|
||||
await backend.db.instrumentsByWork(widget.work.id).get();
|
||||
|
||||
// We don't want to override already selected instruments.
|
||||
if (instruments.isEmpty) {
|
||||
setState(() {
|
||||
instruments = selection;
|
||||
});
|
||||
}
|
||||
}();
|
||||
|
||||
() async {
|
||||
final dbParts = await backend.db.workParts(widget.work.id).get();
|
||||
for (final dbPart in dbParts) {
|
||||
final partInstruments =
|
||||
await backend.db.instrumentsByWork(dbPart.id).get();
|
||||
|
||||
Person partComposer;
|
||||
|
||||
if (dbPart.composer != null) {
|
||||
partComposer =
|
||||
await backend.db.personById(dbPart.composer).getSingle();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
parts.add(PartData(
|
||||
title: dbPart.title,
|
||||
composer: partComposer,
|
||||
instruments: partInstruments,
|
||||
));
|
||||
});
|
||||
}
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> partTiles = [];
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
final part = parts[i];
|
||||
|
||||
partTiles.add(PartTile(
|
||||
key: Key(part.hashCode.toString()),
|
||||
part: part,
|
||||
onMore: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setState) => Dialog(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
WorkProperties(
|
||||
titleController: part.titleController,
|
||||
composer: part.composer,
|
||||
instruments: part.instruments,
|
||||
onComposerChanged: (composer) {
|
||||
setState(() {
|
||||
part.composer = composer;
|
||||
});
|
||||
},
|
||||
onInstrumentsChanged: (instruments) {
|
||||
setState(() {
|
||||
part.instruments = instruments;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onDelete: () {
|
||||
setState(() {
|
||||
parts.removeAt(i);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Work'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () async {
|
||||
final workId = widget.work?.id ?? generateId();
|
||||
|
||||
final model = WorkModel(
|
||||
work: Work(
|
||||
id: workId,
|
||||
title: titleController.text,
|
||||
composer: composer?.id,
|
||||
),
|
||||
instrumentIds: instruments.map((i) => i.id).toList(),
|
||||
);
|
||||
|
||||
final List<WorkModel> partModels = [];
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
final part = parts[i];
|
||||
partModels.add(WorkModel(
|
||||
work: Work(
|
||||
id: generateId(),
|
||||
title: part.titleController.text,
|
||||
composer: part.composer?.id,
|
||||
partOf: workId,
|
||||
partIndex: i,
|
||||
),
|
||||
instrumentIds: part.instruments.map((i) => i.id).toList(),
|
||||
));
|
||||
}
|
||||
|
||||
await backend.db.updateWork(model, partModels);
|
||||
Navigator.pop(context, model.work);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ReorderableListView(
|
||||
header: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
WorkProperties(
|
||||
titleController: titleController,
|
||||
composer: composer,
|
||||
instruments: instruments,
|
||||
onComposerChanged: (newComposer) {
|
||||
setState(() {
|
||||
composer = newComposer;
|
||||
});
|
||||
},
|
||||
onInstrumentsChanged: (newInstruments) {
|
||||
setState(() {
|
||||
instruments = newInstruments;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (parts.length > 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
child: Text(
|
||||
'Parts',
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
children: partTiles,
|
||||
onReorder: (i1, i2) {
|
||||
setState(() {
|
||||
final part = parts.removeAt(i1);
|
||||
final newIndex = i2 > i1 ? i2 - 1 : i2;
|
||||
|
||||
parts.insert(newIndex, part);
|
||||
});
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text('Add part'),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
parts.add(PartData());
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue