changed dialog style

This commit is contained in:
Thibault Deckers 2020-08-08 21:31:19 +09:00
parent a3543a7c69
commit fd572c5838
9 changed files with 145 additions and 124 deletions

View file

@ -22,14 +22,6 @@ class Constants {
static const svgBackground = Colors.white; static const svgBackground = Colors.white;
static const svgColorFilter = ColorFilter.mode(svgBackground, BlendMode.dstOver); static const svgColorFilter = ColorFilter.mode(svgBackground, BlendMode.dstOver);
static const dialogContentHorizontalPadding = EdgeInsets.symmetric(horizontal: 24);
static const dialogActionsPadding = EdgeInsets.symmetric(horizontal: 8);
static const dialogShape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(24),
),
);
static const List<Dependency> androidDependencies = [ static const List<Dependency> androidDependencies = [
Dependency( Dependency(
name: 'CWAC-Document', name: 'CWAC-Document',

View file

@ -1,11 +1,12 @@
import 'dart:io'; import 'dart:io';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/constants.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import '../dialog.dart';
class CreateAlbumDialog extends StatefulWidget { class CreateAlbumDialog extends StatefulWidget {
@override @override
_CreateAlbumDialogState createState() => _CreateAlbumDialogState(); _CreateAlbumDialogState createState() => _CreateAlbumDialogState();
@ -34,63 +35,53 @@ class _CreateAlbumDialogState extends State<CreateAlbumDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AvesDialog(
title: Text('New Album'), title: 'New Album',
content: Container( scrollableContent: [
// workaround because the dialog tries if (_allVolumes.length > 1) ...[
// to size itself to the content intrinsic size, Padding(
// but the `ListView` viewport does not have one padding: AvesDialog.contentHorizontalPadding + EdgeInsets.only(top: 20),
width: double.maxFinite, child: Text('Storage:'),
child: ListView( ),
shrinkWrap: true, ..._allVolumes.map((volume) => RadioListTile<StorageVolume>(
children: [ value: volume,
if (_allVolumes.length > 1) ...[ groupValue: _selectedVolume,
Padding( onChanged: (volume) {
padding: Constants.dialogContentHorizontalPadding, _selectedVolume = volume;
child: Text('Storage:'), _checkAlbumExists();
), setState(() {});
..._allVolumes.map((volume) => RadioListTile<StorageVolume>( },
value: volume, title: Text(
groupValue: _selectedVolume, volume.description,
onChanged: (volume) { softWrap: false,
_selectedVolume = volume; overflow: TextOverflow.fade,
_checkAlbumExists(); maxLines: 1,
setState(() {}); ),
}, subtitle: Text(
title: Text( volume.path,
volume.description, softWrap: false,
softWrap: false, overflow: TextOverflow.fade,
overflow: TextOverflow.fade, maxLines: 1,
maxLines: 1, ),
), )),
subtitle: Text( SizedBox(height: 8),
volume.path, ],
softWrap: false, Padding(
overflow: TextOverflow.fade, padding: AvesDialog.contentHorizontalPadding,
maxLines: 1, child: ValueListenableBuilder<bool>(
), valueListenable: _existsNotifier,
)), builder: (context, exists, child) {
SizedBox(height: 8), return TextField(
], controller: _nameController,
Padding( decoration: InputDecoration(
padding: Constants.dialogContentHorizontalPadding, helperText: exists ? 'Album already exists' : '',
child: ValueListenableBuilder<bool>( ),
valueListenable: _existsNotifier, onChanged: (_) => _checkAlbumExists(),
builder: (context, exists, child) { onSubmitted: (_) => _submit(context),
return TextField( );
controller: _nameController, }),
decoration: InputDecoration(
helperText: exists ? 'Album already exists' : '',
),
onChanged: (_) => _checkAlbumExists(),
onSubmitted: (_) => _submit(context),
);
}),
),
],
), ),
), ],
contentPadding: EdgeInsets.only(top: 20),
actions: [ actions: [
FlatButton( FlatButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
@ -101,8 +92,6 @@ class _CreateAlbumDialogState extends State<CreateAlbumDialog> {
child: Text('Create'.toUpperCase()), child: Text('Create'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
} }

View file

@ -4,10 +4,10 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/services/android_app_service.dart'; 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/utils/constants.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/rename_entry_dialog.dart'; import 'package:aves/widgets/common/action_delegates/rename_entry_dialog.dart';
import 'package:aves/widgets/common/dialog.dart';
import 'package:aves/widgets/common/entry_actions.dart'; import 'package:aves/widgets/common/entry_actions.dart';
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart'; import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
import 'package:aves/widgets/fullscreen/debug.dart'; import 'package:aves/widgets/fullscreen/debug.dart';
@ -120,7 +120,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AvesDialog(
content: Text('Are you sure?'), content: Text('Are you sure?'),
actions: [ actions: [
FlatButton( FlatButton(
@ -132,8 +132,6 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
child: Text('Delete'.toUpperCase()), child: Text('Delete'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
}, },
); );

View file

@ -1,9 +1,10 @@
import 'package:aves/model/settings.dart'; import 'package:aves/model/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/utils/constants.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../dialog.dart';
class GroupCollectionDialog extends StatefulWidget { class GroupCollectionDialog extends StatefulWidget {
@override @override
_GroupCollectionDialogState createState() => _GroupCollectionDialogState(); _GroupCollectionDialogState createState() => _GroupCollectionDialogState();
@ -20,24 +21,14 @@ class _GroupCollectionDialogState extends State<GroupCollectionDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AvesDialog(
title: Text('Group'), title: 'Group',
content: Container( scrollableContent: [
// workaround because the dialog tries _buildRadioListTile(GroupFactor.album, 'By album'),
// to size itself to the content intrinsic size, _buildRadioListTile(GroupFactor.month, 'By month'),
// but the `ListView` viewport does not have one _buildRadioListTile(GroupFactor.day, 'By day'),
width: double.maxFinite, _buildRadioListTile(GroupFactor.none, 'Do not group'),
child: ListView( ],
shrinkWrap: true,
children: [
_buildRadioListTile(GroupFactor.album, 'By album'),
_buildRadioListTile(GroupFactor.month, 'By month'),
_buildRadioListTile(GroupFactor.day, 'By day'),
_buildRadioListTile(GroupFactor.none, 'Do not group'),
],
),
),
contentPadding: EdgeInsets.only(top: 20),
actions: [ actions: [
FlatButton( FlatButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
@ -48,8 +39,6 @@ class _GroupCollectionDialogState extends State<GroupCollectionDialog> {
child: Text('Apply'.toUpperCase()), child: Text('Apply'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
} }

View file

@ -1,8 +1,9 @@
import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_entry.dart';
import 'package:aves/services/android_file_service.dart'; import 'package:aves/services/android_file_service.dart';
import 'package:aves/utils/constants.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../dialog.dart';
mixin PermissionAwareMixin { mixin PermissionAwareMixin {
Future<bool> checkStoragePermission(BuildContext context, Iterable<ImageEntry> entries) { Future<bool> checkStoragePermission(BuildContext context, Iterable<ImageEntry> entries) {
return checkStoragePermissionForAlbums(context, entries.where((e) => e.path != null).map((e) => e.directory).toSet()); return checkStoragePermissionForAlbums(context, entries.where((e) => e.path != null).map((e) => e.directory).toSet());
@ -23,8 +24,8 @@ mixin PermissionAwareMixin {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AvesDialog(
title: Text('Storage Volume Access'), title: 'Storage Volume Access',
content: Text('Please select the $dirDisplayName directory of “$volumeDescription” in the next screen, so that this app can access it and complete your request.'), content: Text('Please select the $dirDisplayName directory of “$volumeDescription” in the next screen, so that this app can access it and complete your request.'),
actions: [ actions: [
FlatButton( FlatButton(
@ -36,8 +37,6 @@ mixin PermissionAwareMixin {
child: Text('OK'.toUpperCase()), child: Text('OK'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
}, },
); );

View file

@ -1,5 +1,5 @@
import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_entry.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RenameEntryDialog extends StatefulWidget { class RenameEntryDialog extends StatefulWidget {
@ -29,7 +29,7 @@ class _RenameEntryDialogState extends State<RenameEntryDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AvesDialog(
content: TextField( content: TextField(
controller: _nameController, controller: _nameController,
autofocus: true, autofocus: true,
@ -44,8 +44,6 @@ class _RenameEntryDialogState extends State<RenameEntryDialog> {
child: Text('Apply'.toUpperCase()), child: Text('Apply'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
} }
} }

View file

@ -15,6 +15,7 @@ import 'package:aves/widgets/album/empty.dart';
import 'package:aves/widgets/common/action_delegates/create_album_dialog.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/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_grid_page.dart'; import 'package:aves/widgets/filter_grid_page.dart';
@ -189,7 +190,7 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AvesDialog(
content: Text('Are you sure you want to delete ${Intl.plural(count, one: 'this item', other: 'these $count items')}?'), content: Text('Are you sure you want to delete ${Intl.plural(count, one: 'this item', other: 'these $count items')}?'),
actions: [ actions: [
FlatButton( FlatButton(
@ -201,8 +202,6 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin {
child: Text('Delete'.toUpperCase()), child: Text('Delete'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
}, },
); );

View file

@ -1,9 +1,10 @@
import 'package:aves/model/settings.dart'; import 'package:aves/model/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/utils/constants.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../dialog.dart';
class SortCollectionDialog extends StatefulWidget { class SortCollectionDialog extends StatefulWidget {
@override @override
_SortCollectionDialogState createState() => _SortCollectionDialogState(); _SortCollectionDialogState createState() => _SortCollectionDialogState();
@ -20,23 +21,13 @@ class _SortCollectionDialogState extends State<SortCollectionDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AvesDialog(
title: Text('Sort'), title: 'Sort',
content: Container( scrollableContent: [
// workaround because the dialog tries _buildRadioListTile(SortFactor.date, 'By date'),
// to size itself to the content intrinsic size, _buildRadioListTile(SortFactor.size, 'By size'),
// but the `ListView` viewport does not have one _buildRadioListTile(SortFactor.name, 'By album & file name'),
width: double.maxFinite, ],
child: ListView(
shrinkWrap: true,
children: [
_buildRadioListTile(SortFactor.date, 'By date'),
_buildRadioListTile(SortFactor.size, 'By size'),
_buildRadioListTile(SortFactor.name, 'By album & file name'),
],
),
),
contentPadding: EdgeInsets.only(top: 20),
actions: [ actions: [
FlatButton( FlatButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
@ -47,8 +38,6 @@ class _SortCollectionDialogState extends State<SortCollectionDialog> {
child: Text('Apply'.toUpperCase()), child: Text('Apply'.toUpperCase()),
), ),
], ],
actionsPadding: Constants.dialogActionsPadding,
shape: Constants.dialogShape,
); );
} }

View file

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class AvesDialog extends AlertDialog {
static const contentHorizontalPadding = EdgeInsets.symmetric(horizontal: 24);
AvesDialog({
String title,
List<Widget> scrollableContent,
Widget content,
@required List<Widget> actions,
}) : assert((scrollableContent != null) ^ (content != null)),
super(
title: title != null ? DialogTitle(title: title) : null,
titlePadding: EdgeInsets.zero,
// the `scrollable` flag of `AlertDialog` makes it
// scroll both the title and the content together,
// and overflow feedback ignores the dialog shape,
// so we restrict scrolling to the content instead
content: scrollableContent != null
? Builder(
builder: (context) => Container(
// workaround because the dialog tries
// to size itself to the content intrinsic size,
// but the `ListView` viewport does not have one
width: 1,
child: ListView(
shrinkWrap: true,
children: scrollableContent,
),
),
)
: content,
contentPadding: scrollableContent != null ? EdgeInsets.zero : EdgeInsets.fromLTRB(24, 20, 24, 24),
actions: actions,
actionsPadding: EdgeInsets.symmetric(horizontal: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(24),
),
),
);
}
class DialogTitle extends StatelessWidget {
final String title;
const DialogTitle({@required this.title});
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'Concourse Caps',
),
),
),
Divider(height: 1),
],
);
}
}