[client] Responsive destination picker

This commit is contained in:
Abhishek Shroff
2024-11-26 23:16:18 +05:30
parent bd476652bd
commit 3b222fa36e
@@ -56,7 +56,9 @@ class _DestinationPickerState extends State<DestinationPicker> {
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context) => MediaQuery.sizeOf(context).width <= 600 ? buildScaffold(context) : buildDialog(context);
Widget buildScaffold(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Builder(builder: (context) {
@@ -89,96 +91,156 @@ class _DestinationPickerState extends State<DestinationPicker> {
})
],
),
body: FocusScope(
child: Actions(
actions: {
NavUpIntent: CallbackAction<NavUpIntent>(onInvoke: (i) async {
final parentId = context.read<ExplorerState>().folder?.parent;
if (parentId != null) {
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
}
node.requestFocus();
return null;
}),
FocusDownIntent: CallbackAction<FocusDownIntent>(
onInvoke: (i) async {
context.read<ExplorerController>().updateSelection((i) => i + 1, SelectionMode.single, false);
node.requestFocus();
return null;
},
),
FocusUpIntent: CallbackAction<FocusUpIntent>(
onInvoke: (i) async {
context.read<ExplorerController>().updateSelection((i) => i - 1, SelectionMode.single, false);
node.requestFocus();
return null;
},
),
ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: (i) async {
final selected = context.read<ExplorerState>().selectedSingle;
if (selected != null && selected.dir) {
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: selected.id));
}
return null;
}),
DismissIntent: CallbackAction<DismissIntent>(onInvoke: (i) => Navigator.of(context).pop()),
},
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Focus(
autofocus: true,
focusNode: node,
child: ExcludeFocus(child: const DestinationFolderView()),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(onPressed: Navigator.of(context).pop, child: Text('Cancel')),
ElevatedButton(
onPressed: context.select<ExplorerState, bool>((state) => state.folderId == widget.initialFolderId)
? null
: () => Navigator.of(context).pop(context.read<ExplorerState>().folderId),
child: Text('OK'),
),
],
),
)
],
body: _buildContents(context),
);
}
Widget buildDialog(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(32.0),
child: _ActionHandler(
focusNode: node,
child: AlertDialog(
titlePadding: EdgeInsets.zero,
contentPadding: EdgeInsets.zero,
title: Builder(
builder: (context) {
final name = context.select<ExplorerState, String?>((state) => state.folder?.name);
return ListTile(
leading: BackButton(onPressed: () {
final parentId = context.read<ExplorerState>().folder?.parent;
if (parentId != null) {
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
node.requestFocus();
} else {
context.pop();
}
}),
title: Text(name ?? ''),
subtitle: const Text('Pick Destination'),
trailing: Builder(builder: (context) {
final folderId = context.select<ExplorerState, String?>((state) => state.folderId);
return IconButton(
onPressed: folderId == null ? null : () => createDirectory(context, folderId),
icon: Icon(Icons.create_new_folder_outlined),
);
}),
);
},
),
content: SizedBox(width: 360, height: 600, child: _buildContents(context)),
),
),
);
}
Widget _buildContents(BuildContext context) {
return FocusScope(
child: _ActionHandler(
focusNode: node,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(child: _FolderListView(focusNode: node)),
Divider(thickness: 0, height: 0),
Padding(
padding: const EdgeInsets.all(12),
child: OverflowBar(
alignment: MainAxisAlignment.end,
spacing: 8,
children: [
TextButton(onPressed: Navigator.of(context).pop, child: Text('Cancel')),
ElevatedButton(
onPressed: context.select<ExplorerState, bool>((state) => state.folderId == widget.initialFolderId)
? null
: () => Navigator.of(context).pop(context.read<ExplorerState>().folderId),
child: Text('OK'),
),
],
),
)
],
),
),
);
}
}
class DestinationFolderView extends StatelessWidget {
const DestinationFolderView({super.key});
class _FolderListView extends StatelessWidget {
final FocusNode focusNode;
const _FolderListView({required this.focusNode});
@override
Widget build(BuildContext context) {
final items = context.select<ExplorerState, List<Resource>>((state) => state.resources);
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
final r = items[index];
return Builder(builder: (context) {
final selected = context.select<ExplorerState, bool>((state) => state.selectedIds.contains(r.id));
return ListTile(
key: ValueKey(r.id),
visualDensity: VisualDensity.compact,
selected: selected,
leading: r.getIcon(),
title: Text(r.name),
enabled: r.dir,
onTap: () => context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: r.id)),
);
});
},
itemCount: items.length,
return Focus(
autofocus: true,
focusNode: focusNode,
child: ExcludeFocus(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
final r = items[index];
return Builder(builder: (context) {
final selected = context.select<ExplorerState, bool>((state) => state.selectedIds.contains(r.id));
return ListTile(
key: ValueKey(r.id),
visualDensity: VisualDensity.compact,
selected: selected,
leading: r.getIcon(),
title: Text(r.name),
enabled: r.dir,
onTap: () => context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: r.id)),
);
});
},
itemCount: items.length,
),
),
);
}
}
class _ActionHandler extends StatelessWidget {
final Widget child;
final FocusNode focusNode;
const _ActionHandler({required this.child, required this.focusNode});
@override
Widget build(BuildContext context) {
return Actions(
actions: {
NavUpIntent: CallbackAction<NavUpIntent>(onInvoke: (i) async {
final parentId = context.read<ExplorerState>().folder?.parent;
if (parentId != null) {
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
}
focusNode.requestFocus();
return null;
}),
FocusDownIntent: CallbackAction<FocusDownIntent>(
onInvoke: (i) async {
context.read<ExplorerController>().updateSelection((i) => i + 1, SelectionMode.single, false);
focusNode.requestFocus();
return null;
},
),
FocusUpIntent: CallbackAction<FocusUpIntent>(
onInvoke: (i) async {
context.read<ExplorerController>().updateSelection((i) => i - 1, SelectionMode.single, false);
focusNode.requestFocus();
return null;
},
),
ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: (i) async {
final selected = context.read<ExplorerState>().selectedSingle;
if (selected != null && selected.dir) {
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: selected.id));
}
return null;
}),
DismissIntent: CallbackAction<DismissIntent>(onInvoke: (i) => Navigator.of(context).pop()),
},
child: child,
);
}
}