feat: Implement bulk label forms (except tags)

This commit is contained in:
Anton Stubenbord
2023-04-11 01:16:20 +02:00
parent f2fa4e16de
commit 83d8abeae2
12 changed files with 447 additions and 308 deletions

View File

@@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class FullscreenSelectionForm extends StatefulWidget {
final FocusNode? focusNode;
final TextEditingController? controller;
final String hintText;
final Widget leadingIcon;
final bool autofocus;
final VoidCallback? onTextFieldCleared;
final List<Widget> trailingActions;
final Widget Function(BuildContext context, int index) selectionBuilder;
final int selectionCount;
final void Function(String value)? onKeyboardSubmit;
final Widget? floatingActionButton;
const FullscreenSelectionForm({
super.key,
this.focusNode,
this.controller,
required this.hintText,
required this.leadingIcon,
this.autofocus = true,
this.onTextFieldCleared,
this.trailingActions = const [],
required this.selectionBuilder,
required this.selectionCount,
this.onKeyboardSubmit,
this.floatingActionButton,
});
@override
State<FullscreenSelectionForm> createState() =>
_FullscreenSelectionFormState();
}
class _FullscreenSelectionFormState extends State<FullscreenSelectionForm> {
late final FocusNode _focusNode;
late final TextEditingController _controller;
bool _showClearIcon = false;
@override
void initState() {
super.initState();
_focusNode = widget.focusNode ?? FocusNode();
_controller = (widget.controller ?? TextEditingController())
..addListener(() {
setState(() {
_showClearIcon = _controller.text.isNotEmpty;
});
});
if (widget.autofocus) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
//Delay keyboard popup to ensure open animation is finished before.
Future.delayed(
const Duration(milliseconds: 200),
() => _focusNode.requestFocus(),
);
});
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
floatingActionButton: widget.floatingActionButton,
appBar: AppBar(
backgroundColor: theme.colorScheme.surface,
toolbarHeight: 72,
leading: BackButton(
color: theme.colorScheme.onSurface,
),
title: TextFormField(
focusNode: _focusNode,
controller: _controller,
onFieldSubmitted: (value) {
FocusScope.of(context).unfocus();
widget.onKeyboardSubmit?.call(value);
},
autofocus: true,
style: theme.textTheme.bodyLarge?.apply(
color: theme.colorScheme.onSurface,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
hintStyle: theme.textTheme.bodyLarge?.apply(
color: theme.colorScheme.onSurfaceVariant,
),
icon: widget.leadingIcon,
hintText: widget.hintText,
border: InputBorder.none,
),
textInputAction: TextInputAction.done,
),
actions: [
if (_showClearIcon)
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_controller.clear();
widget.onTextFieldCleared?.call();
},
),
...widget.trailingActions,
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Divider(
color: theme.colorScheme.outline,
),
),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: widget.selectionCount,
itemBuilder: (BuildContext context, int index) {
final highlight =
AutocompleteHighlightedOption.of(context) == index;
if (highlight) {
SchedulerBinding.instance
.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(
context,
alignment: 0,
);
});
}
return widget.selectionBuilder(context, index);
},
),
),
],
),
);
}
}