junny_form 0.0.17
junny_form: ^0.0.17 copied to clipboard
A flexible Flutter form management library that supports centralized field management, async validation, and custom field types.
example/lib/main.dart
import 'dart:math' as math;
import 'package:example/address/address_utils.dart';
import 'package:example/pages/dict_enum_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:junny_form/junny_form.dart';
import 'pages/home_page.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
JunnyFormGlobalConfig.instance.init(
getDictByEnum: (enumValue, _) => [
{
'dataCode': '01',
'dataName': '1%',
},
{
'dataCode': '02',
'dataName': '3%',
},
{
'dataCode': '03',
'dataName': '5%',
},
],
attachmentsFormatter: (Iterable<Map<String, dynamic>>? attachments) {
return attachments?.isNotEmpty ?? false
? '${attachments?.length ?? 0} 个附件'
: null;
},
// 演示全局配置子表保存按钮文本
subtableSaveButtonTextBuilder: (context) => '完成',
// getAttachmentsData: (attachment, cancelToken) async {
// final attachmentIds = attachment.splitIterable<int>();
// if (attachmentIds.isEmpty) {
// return [];
// } else {
// // 做一个模拟数据
// return List.generate(
// attachmentIds.length,
// (index) => {
// 'id': attachmentIds.elementAt(index),
// 'sysFileMetadataId': attachmentIds.elementAt(index),
// 'originalName': '附件${attachmentIds.elementAt(index)}.pdf',
// 'fileSize': (attachmentIds.elementAt(index) * 100 * 1024),
// },
// );
// }
// },
selectAttachments:
(BuildContext context, AttachmentTapParams params) async {
// 模拟选择, 结合maxCount 返回结果
// 如果maxCount 为 3,则返回 3 个附件
final int maxCount = params.maxCount;
final bool enabled = params.enabled;
final List<Map<String, dynamic>> attachments = params.attachments;
if (!enabled) {
if (attachments.isNotEmpty) {
// toast提示
JunnyToastUtils.instance.show('附件不可编辑, 但可以预览');
}
return attachments;
}
// v3版本的模拟数据:包含完整的附件信息
final List<Map<String, dynamic>> result = [
{
'id': 1,
'sysFileMetadataId': 'file_1',
'originalName': '合同附件1.pdf',
'fileSize': 102400,
'pageUniqueCode': 'CONTRACT_REG_INCOME',
'columnCode': params.title ?? 'attachment',
},
{
'id': 2,
'sysFileMetadataId': 'file_2',
'originalName': '合同附件2.jpg',
'fileSize': 204800,
'pageUniqueCode': 'CONTRACT_REG_INCOME',
'columnCode': params.title ?? 'attachment',
},
{
'id': 3,
'sysFileMetadataId': 'file_3',
'originalName': '合同附件3.docx',
'fileSize': 307200,
'pageUniqueCode': 'CONTRACT_REG_INCOME',
'columnCode': params.title ?? 'attachment',
},
{
'id': 4,
'sysFileMetadataId': 'file_4',
'originalName': '合同附件4.xlsx',
'fileSize': 409600,
'pageUniqueCode': 'CONTRACT_REG_INCOME',
'columnCode': params.title ?? 'attachment',
},
{
'id': 5,
'sysFileMetadataId': 'file_5',
'originalName': '合同附件5.png',
'fileSize': 512000,
'pageUniqueCode': 'CONTRACT_REG_INCOME',
'columnCode': params.title ?? 'attachment',
},
];
// 修复:确保随机选择的起始位置加上 maxCount 不会超出数组长度
final int availableCount = math.min(maxCount, result.length);
final int startIndex =
math.Random().nextInt(result.length - availableCount + 1);
// 选择不超过 maxCount 个附件
return result.sublist(
startIndex,
startIndex + math.Random().nextInt(availableCount) + 1,
);
},
isTaxRateEnum: (enumValue) => enumValue == ExampleEnum.taxRate,
);
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late AddressUtils _addressUtils;
@override
void initState() {
super.initState();
init();
}
Future<void> init() async {
_addressUtils = await AddressUtils.create();
// 组件库初始化全局配置
JunnyGlobalWidgetConfig.instance.initialize(
addressChildrenDataQuery: ([int? parentId]) => _addressUtils
.getChildrenAddresses(parentId ?? 0)
.map((e) => e.toJson())
.toList(),
addressDataInitializer: () async {
// 如果没有初始化地址数据
if (_addressUtils.count() <= 0) {
return _addressUtils.initAddressData();
}
},
addressDataQuery: (int id) => _addressUtils.getAddress(id)?.toJson(),
addressDataConverter: (String data) => _addressUtils.getAddressData(data),
// emptyIconBuilder: (BuildContext context) =>
// JunnyKitAssets.images.common.noData.image(width: 50, height: 50),
// emptyTextBuilder: (BuildContext context) =>
// 'noData'.withTips.translate(context),
// yearPickerPresenter: ({
// required BuildContext context,
// required Widget Function(ValueSetter<int?>? onYearChanged) picker,
// }) async {
// int? selectedYear;
// void onYearChanged(int? year) {
// selectedYear = year;
// }
// final int? result =
// await JunnyDialogUtils.instance.showCommonDialog<int>(
// contentWidget: picker(onYearChanged),
// title: MaterialLocalizations.of(context).selectYearSemanticsLabel,
// onConfirm: () =>
// JunnyDialogUtils.instance.dismiss<int>(result: selectedYear),
// );
// return result;
// },
);
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueAccent),
useMaterial3: true,
),
locale: const Locale.fromSubtags(languageCode: 'zh'),
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale('zh', 'CN'),
],
navigatorObservers: <NavigatorObserver>[
FlutterSmartDialog.observer,
],
home: const HomePage(),
builder: FlutterSmartDialog.init(),
);
}
}