Procházet zdrojové kódy

VCheckBoxCellGroup

Melon před 1 rokem
rodič
revize
0128364180

+ 106 - 0
lib/components/checkbox_cell/cell.dart

@@ -0,0 +1,106 @@
+import 'package:flutter/material.dart';
+
+typedef VCheckBoxCellChangeCallback<T> = void Function(
+    T? value, bool isChecked);
+
+class VCheckBoxCell<T> extends StatefulWidget {
+  final String label;
+  final T? value;
+  final bool isChecked;
+  final bool isDisabled;
+  final VCheckBoxCellChangeCallback<T>? onChanged;
+
+  const VCheckBoxCell({
+    super.key,
+    required this.label,
+    required this.value,
+    required this.isChecked,
+    required this.isDisabled,
+    this.onChanged,
+  });
+
+  @override
+  State<StatefulWidget> createState() => _VCheckBoxCellState<T>();
+}
+
+class _VCheckBoxCellState<T> extends State<VCheckBoxCell<T>> {
+  static const _horizontalPadding = 24.0;
+  late bool _isChecked;
+
+  static const _height = 56.0;
+
+  @override
+  void initState() {
+    _isChecked = widget.isChecked;
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final iconWidget = _CheckStatusIcon(
+      isChecked: _isChecked,
+      isDisabled: widget.isDisabled,
+    );
+    final labelWidget = Text(
+      widget.label,
+      style: TextStyle(
+        color: _isChecked ? Theme.of(context).primaryColor : Colors.black87,
+        fontSize: 20,
+      ),
+    );
+    return Material(
+      child: Ink(
+        child: InkWell(
+          onTap: widget.isDisabled ? null : _onClick,
+          child: Container(
+            height: _height,
+            padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding),
+            color: _isChecked ? Theme.of(context).secondaryHeaderColor : null,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                labelWidget,
+                iconWidget,
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  void _onClick() {
+    setState(() {
+      _isChecked = !_isChecked;
+      widget.onChanged?.call(widget.value, _isChecked);
+    });
+  }
+}
+
+class _CheckStatusIcon extends StatelessWidget {
+  final bool isChecked;
+  final bool isDisabled;
+
+  const _CheckStatusIcon({
+    super.key,
+    required this.isChecked,
+    required this.isDisabled,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    if (isChecked) {
+      return Icon(
+        Icons.check_outlined,
+        color: Theme.of(context).primaryColor,
+        size: 24,
+      );
+    } else {
+      return const Icon(
+        Icons.check_box_outline_blank_outlined,
+        color: Colors.grey,
+        size: 24,
+      );
+    }
+  }
+}

+ 1 - 0
lib/components/checkbox_cell/dialog.dart

@@ -0,0 +1 @@
+

+ 128 - 0
lib/components/checkbox_cell/drawer.dart

@@ -0,0 +1,128 @@
+import 'package:flutter/material.dart';
+import '../dynamic_drawer.dart';
+
+import 'group.dart';
+
+class VDrawerCheckBoxCellGroup<T, TValue> extends StatefulWidget {
+  final GlobalKey<ScaffoldState> scaffoldKey;
+  final String? title;
+  final List<CheckBoxCellGroupItem<T>> source;
+
+  /// 选项文本提取函数
+  final String Function(T data) labelGetter;
+
+  /// 选项值提取函数
+  final TValue Function(T data) valueGetter;
+
+  final VCheckBoxCellGroupCheckChanged<T, TValue>? onChanged;
+
+  final VoidCallback? onConfirm;
+
+  const VDrawerCheckBoxCellGroup({
+    super.key,
+    this.title,
+    required this.source,
+    required this.labelGetter,
+    required this.valueGetter,
+    this.onChanged,
+    required this.scaffoldKey,
+    this.onConfirm,
+  });
+
+  @override
+  State<StatefulWidget> createState() => _DrawerGroupState<T, TValue>();
+}
+
+class _DrawerGroupState<T, TValue>
+    extends State<VDrawerCheckBoxCellGroup<T, TValue>> {
+  @override
+  Widget build(BuildContext context) {
+    return Drawer(
+      shadowColor: Colors.black.withOpacity(.1),
+      backgroundColor: Colors.white,
+      shape: RoundedRectangleBorder(
+        borderRadius: BorderRadius.circular(16),
+      ),
+      elevation: 0,
+      width: 520,
+      child: Container(
+        alignment: Alignment.topLeft,
+        padding: const EdgeInsets.symmetric(vertical: 12),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Container(
+              padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 4),
+              child: Row(
+                mainAxisSize: MainAxisSize.max,
+                mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                children: [
+                  widget.title != null
+                      ? Text(
+                          widget.title!,
+                          style: const TextStyle(
+                            color: Colors.black,
+                            fontSize: 24,
+                          ),
+                        )
+                      : const SizedBox(width: 1),
+                  IconButton(
+                    onPressed: () {
+                      VDynamicDrawerWrapper.hide(
+                          scaffoldKey: widget.scaffoldKey);
+                    },
+                    icon: Icon(
+                      Icons.close,
+                      color: Colors.grey.shade600,
+                      size: 36,
+                    ),
+                    // style: ButtonStyle(
+                    //   shape: MaterialStatePropertyAll(
+                    //     RoundedRectangleBorder(
+                    //       side: BorderSide(color: Colors.grey.shade600),
+                    //       borderRadius: BorderRadius.circular(48),
+                    //     ),
+                    //   ),
+                    // ),
+                  ),
+                ],
+              ),
+            ),
+            Expanded(
+              child: Scrollbar(
+                thumbVisibility: true,
+                child: VCheckBoxCellGroup<T, TValue>(
+                  labelGetter: widget.labelGetter,
+                  valueGetter: widget.valueGetter,
+                  source: widget.source,
+                  onChanged: widget.onChanged,
+                  isScrollable: true,
+                ),
+              ),
+            ),
+            Container(
+              padding:
+                  const EdgeInsets.symmetric(horizontal: 18).copyWith(top: 8),
+              alignment: Alignment.center,
+              child: TextButton(
+                onPressed: () {
+                  widget.onConfirm?.call();
+                  VDynamicDrawerWrapper.hide(scaffoldKey: widget.scaffoldKey);
+                },
+                child: Container(
+                  width: double.infinity,
+                  height: 38,
+                  alignment: Alignment.center,
+                  child: const Text(
+                    "确定",
+                    style: TextStyle(fontSize: 20),
+                  ),
+                ),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 177 - 0
lib/components/checkbox_cell/group.dart

@@ -0,0 +1,177 @@
+import 'package:flutter/material.dart';
+
+import '../alert_dialog.dart';
+import 'cell.dart';
+
+typedef VCheckBoxCellGroupItemBuilder<T> = Widget Function(T data);
+
+typedef VCheckBoxCellGroupCheckChanged<T, TValue> = void Function(
+  TValue value,
+  bool isChecked,
+  T data,
+  List<TValue> checkedValues,
+);
+
+class VDialogCheckBoxCellGroup<T, TValue> extends StatefulWidget {
+  final String? title;
+  final List<CheckBoxCellGroupItem<T>> source;
+
+  /// 选项文本提取函数
+  final String Function(T data) labelGetter;
+
+  /// 选项值提取函数
+  final TValue Function(T data) valueGetter;
+
+  final VCheckBoxCellGroupCheckChanged<T, TValue>? onChanged;
+
+  const VDialogCheckBoxCellGroup({
+    super.key,
+    this.title,
+    required this.source,
+    required this.labelGetter,
+    required this.valueGetter,
+    this.onChanged,
+  });
+
+  Future<List<V>> show<V>() async {
+    final result = await VAlertDialog.showDialog<List<V>>(this);
+    return result!;
+  }
+
+  @override
+  State<StatefulWidget> createState() => _DialogGroupState<T, TValue>();
+}
+
+class _DialogGroupState<T, TValue>
+    extends State<VDialogCheckBoxCellGroup<T, TValue>> {
+  @override
+  Widget build(BuildContext context) {
+    return VAlertDialog(
+      title: widget.title,
+      // contentPadding: EdgeInsets.symmetric(horizontal: 20),
+      content: Scrollbar(
+        thumbVisibility: true,
+        child: VCheckBoxCellGroup<T, TValue>(
+          labelGetter: widget.labelGetter,
+          valueGetter: widget.valueGetter,
+          source: widget.source,
+          onChanged: widget.onChanged,
+          isScrollable: true,
+        ),
+      ),
+    );
+  }
+}
+
+class CheckBoxCellGroupItem<T> {
+  final T data;
+  final bool isChecked;
+  final bool isDisabled;
+
+  CheckBoxCellGroupItem(
+    this.data, {
+    this.isChecked = false,
+    this.isDisabled = false,
+  });
+}
+
+class VCheckBoxCellGroup<T, TValue> extends StatefulWidget {
+  final List<CheckBoxCellGroupItem<T>> source;
+
+  /// 选项文本提取函数
+  final String Function(T data) labelGetter;
+
+  /// 选项值提取函数
+  final TValue Function(T data) valueGetter;
+
+  final VCheckBoxCellGroupCheckChanged<T, TValue>? onChanged;
+
+  final bool isScrollable;
+
+  const VCheckBoxCellGroup({
+    super.key,
+    required this.source,
+    required this.labelGetter,
+    required this.valueGetter,
+    this.onChanged,
+    this.isScrollable = true,
+  });
+
+  @override
+  State<StatefulWidget> createState() => _VCheckBoxCellGroupState<T, TValue>();
+}
+
+class _VCheckBoxCellGroupState<T, TValue>
+    extends State<VCheckBoxCellGroup<T, TValue>> {
+  List<TValue> _checkedValues = [];
+
+  @override
+  void initState() {
+    _checkedValues = widget.source
+        .where((e) => e.isChecked)
+        .map((e) => widget.valueGetter.call(e.data))
+        .toList();
+
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final divider = Divider(
+      thickness: 1,
+      color: Colors.grey.shade300,
+      height: 2,
+    );
+
+    final children = <Widget>[];
+    final count = widget.source.length;
+    for (var i = 0; i < count; i++) {
+      if (i > 0) {
+        children.add(divider);
+      }
+      final item = widget.source[i];
+      children.add(_buildItemWidget(item));
+    }
+
+    if (widget.isScrollable) {
+      return ListView(
+        shrinkWrap: true,
+        children: children,
+      );
+    } else {
+      return Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        mainAxisSize: MainAxisSize.min,
+        children: children,
+      );
+    }
+  }
+
+  Widget _buildItemWidget(CheckBoxCellGroupItem<T> item) {
+    final label = widget.labelGetter.call(item.data);
+    final value = widget.valueGetter.call(item.data);
+    final isChecked = _checkedValues.contains(value);
+    return VCheckBoxCell<TValue>(
+      label: label,
+      value: value,
+      isChecked: isChecked,
+      isDisabled: item.isDisabled,
+      onChanged: (_, isChecked) {
+        _onCheckChanged(value, item.data, isChecked);
+      },
+    );
+  }
+
+  void _onCheckChanged(TValue value, T data, bool isChecked) {
+    setState(() {
+      if (isChecked) {
+        if (_checkedValues.contains(value) == false) {
+          _checkedValues.add(value);
+        }
+      } else {
+        _checkedValues.remove(value);
+      }
+      widget.onChanged?.call(value, isChecked, data, _checkedValues);
+    });
+  }
+}

+ 4 - 0
lib/components/checkbox_cell/index.dart

@@ -0,0 +1,4 @@
+export 'cell.dart';
+export 'group.dart';
+export 'drawer.dart';
+export 'dialog.dart';

+ 0 - 0
lib/components/dynamic_drawer.dart.dart → lib/components/dynamic_drawer.dart


+ 1 - 1
lib/pages/home/view.dart

@@ -1,6 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
-import 'package:vnoteapp/components/dynamic_drawer.dart.dart';
+import 'package:vnoteapp/components/dynamic_drawer.dart';
 import 'package:vnoteapp/pages/home/controller.dart';
 
 import 'widgets/avatar.dart';

+ 1 - 1
lib/pages/patient/create/view_new.dart

@@ -9,7 +9,7 @@ import 'package:vnoteapp/components/checkbox_button.dart';
 import 'package:vnoteapp/components/dialog_date.dart';
 import 'package:vnoteapp/components/dialog_input.dart';
 import 'package:vnoteapp/components/dialog_select.dart';
-import 'package:vnoteapp/components/dynamic_drawer.dart.dart';
+import 'package:vnoteapp/components/dynamic_drawer.dart';
 import 'package:vnoteapp/consts/nations.dart';
 import 'package:vnoteapp/consts/rpc_enum_labels.dart';
 import 'package:vnoteapp/pages/controllers/crowd_labels.dart';