import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_common/flutter_common.dart'; import 'package:get/get.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return GetMaterialApp( title: 'flutter_common Demo', debugShowCheckedModeBanner: false, builder: EasyLoading.init(), theme: ThemeData( scaffoldBackgroundColor: const Color(0xFFF5F7FB), colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4D6FD5)), cardTheme: const CardThemeData( color: Colors.white, margin: EdgeInsets.zero, elevation: 0, ), ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { static const String _ossAccessKeyId = 'LTAI5tRJh3W4TrfQC3mDK9Vp'; static const String _ossHost = 'https://jingheyijia-cop.oss-cn-chengdu.aliyuncs.com'; static const String _bindHost = 'https://static.cop.jingheyijia.com'; static const String _policy = 'eyJleHBpcmF0aW9uIjoiMjAyNi0wNi0zMFQyMTo0OTozNVoiLCJjb25kaXRpb25zIjpbWyJzdGFydHMtd2l0aCIsIiRrZXkiLCJ3eGFwcC1tYXAyL3VwbG9hZC8iXV19'; static const String _signature = 're2VV8BiN5oS2altXSmiTG/Y0wc='; static const String _ossDirectory = 'wxapp-map2/upload/'; static const String _callback = 'eyJjYWxsYmFja1VybCI6Imh0dHBzOi8vdGVzdGluZy52aWN0b3JtZW4uY29tL2FwaS9haXN0b3JlL29zcy9jYWxsYmFjayIsImNhbGxiYWNrQm9keSI6ImZpbGVuYW1lPSR7b2JqZWN0fVx1MDAyNnNpemU9JHtzaXplfVx1MDAyNm1pbWVUeXBlPSR7bWltZVR5cGV9XHUwMDI2aGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH1cdTAwMjZ3aWR0aD0ke2ltYWdlSW5mby53aWR0aH0iLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0='; static const String _seedImageUrl = '$_bindHost/wxapp-map2/upload/moment/2025625/app-yctgxYDwcPkh.jpg'; String _singleDateText = '未选择'; String _rangeDateText = '未选择'; String _imageStatus = '已预置 1 张示例图,点击缩略图预览,长按大图保存到相册'; late List _imageUrls; @override void initState() { super.initState(); _imageUrls = [_seedImageUrl]; } Future _uploadImages() async { await UploadImagesTool.uploadImagesTool( context: context, max: 9, isShowLoading: true, oSSAccessKeyId: _ossAccessKeyId, policy: _policy, callback: _callback, signature: _signature, ossDirectory: _ossDirectory, ossHost: _ossHost, chooseImagesTap: (list) { final uploadedUrls = List.from(list as List) .where((item) => item.isNotEmpty) .toList(); if (uploadedUrls.isEmpty) { return; } setState(() { _imageUrls = [..._imageUrls, ...uploadedUrls]; _imageStatus = '已上传 ${uploadedUrls.length} 张图片,当前共 ${_imageUrls.length} 张'; }); }, ); } void _openPreview(int index) { if (_imageUrls.isEmpty) { return; } LookImagesTool.lookImages( listData: _imageUrls, currentPage: index, isShowEdit: true, oSSAccessKeyId: _ossAccessKeyId, policy: _policy, callback: _callback, signature: _signature, ossDirectory: _ossDirectory, ossHost: _ossHost, onEditCallBack: (imageUrl, currentIndex) { setState(() { _imageUrls[currentIndex] = imageUrl; _imageStatus = '第 ${currentIndex + 1} 张图片已重新编辑并上传'; }); }, ); } void _removeImage(int index) { setState(() { _imageUrls.removeAt(index); _imageStatus = _imageUrls.isEmpty ? '暂无图片,请先上传后再体验预览和下载' : '已删除 1 张图片,当前剩余 ${_imageUrls.length} 张'; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('flutter_common 示例首页'), centerTitle: true, ), body: SafeArea( child: ListView( padding: const EdgeInsets.all(16), children: [ _buildIntroCard(), const SizedBox(height: 16), _buildImageDemoCard(), const SizedBox(height: 16), _buildDemoCard( title: '单日选择组件', description: '使用 `CalendarChooseWidget` 的单选模式,适合筛选某一天的数据。', child: CalendarChooseWidget( chooseIndex: 1, selectedDate: DateTime.now(), dateTimeUtilsType: DateTimeUtilsType.yearMonthDayWord, fontSize: 18, fontWeight: FontWeight.w600, tapAction: (value) { final startTime = value['startTime'] as DateTime?; setState(() { _singleDateText = _formatDateWithWeekday(startTime); }); }, ), resultText: _singleDateText, ), const SizedBox(height: 16), _buildDemoCard( title: '区间选择组件', description: '默认展示开始和结束日期,适合做报表、订单或运营时间筛选。', child: CalendarChooseWidget( selectedDate: DateTime.now(), dateTimeUtilsType: DateTimeUtilsType.yearMonthDay, fontSize: 18, fontWeight: FontWeight.w600, tapAction: (value) { final startTime = value['startTime'] as DateTime?; final endTime = value['endTime'] as DateTime?; setState(() { _rangeDateText = '${_formatDate(startTime)} 至 ${_formatDate(endTime)}'; }); }, ), resultText: _rangeDateText, ), ], ), ), ); } Widget _buildIntroCard() { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: const [ Text( '项目结构速览', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF1A1A1A), ), ), SizedBox(height: 12), Text( 'lib/calendarcalendar 提供日期筛选能力,' 'lib/upload_image 包含 OSS 上传、图片预览、编辑和下载到相册的能力,' 'lib/utils 则放了日期、弹窗等通用工具。', style: TextStyle( fontSize: 15, height: 1.6, color: Color(0xFF4F5B67), ), ), ], ), ), ); } Widget _buildImageDemoCard() { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'LookImagesTool 图片预览', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Color(0xFF1A1A1A), ), ), const SizedBox(height: 8), const Text( '这个示例使用你提供的 OSS 配置上传图片,点击缩略图进入 `LookImagesTool` 预览,支持编辑后重新上传,也支持长按大图保存到手机相册。', style: TextStyle( fontSize: 14, height: 1.6, color: Color(0xFF5C6670), ), ), const SizedBox(height: 14), Wrap( spacing: 8, runSpacing: 8, children: [ const _InfoChip( label: 'Host', value: 'jingheyijia-cop.oss-cn-chengdu.aliyuncs.com', ), _InfoChip(label: 'Bind', value: _bindHost), const _InfoChip(label: '目录', value: _ossDirectory), ], ), const SizedBox(height: 16), FilledButton.icon( onPressed: _uploadImages, icon: const Icon(Icons.cloud_upload_outlined), label: const Text('上传图片到 OSS'), ), const SizedBox(height: 16), Wrap( spacing: 12, runSpacing: 12, children: [ for (int index = 0; index < _imageUrls.length; index++) _buildImageTile( imageUrl: _imageUrls[index], index: index, ), ], ), const SizedBox(height: 14), Text( _imageStatus, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF4D6FD5), ), ), ], ), ), ); } Widget _buildImageTile({ required String imageUrl, required int index, }) { return SizedBox( width: 104, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(16), child: Material( color: Colors.white, child: InkWell( onTap: () => _openPreview(index), child: Stack( children: [ SizedBox( width: 104, height: 104, child: Image.network( imageUrl, fit: BoxFit.cover, errorBuilder: (_, __, ___) { return Container( color: const Color(0xFFF1F4FA), alignment: Alignment.center, child: const Icon( Icons.broken_image_outlined, color: Color(0xFF7A8694), ), ); }, ), ), Positioned( left: 8, top: 8, child: Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(12), ), child: Text( '${index + 1}', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ), Positioned( right: 6, top: 6, child: GestureDetector( onTap: () => _removeImage(index), child: Container( width: 24, height: 24, decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.45), shape: BoxShape.circle, ), child: const Icon( Icons.close, size: 14, color: Colors.white, ), ), ), ), ], ), ), ), ), const SizedBox(height: 8), Text( '点击预览', style: const TextStyle( fontSize: 12, color: Color(0xFF5C6670), ), ), ], ), ); } Widget _buildDemoCard({ required String title, required String description, required Widget child, required String resultText, }) { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Color(0xFF1A1A1A), ), ), const SizedBox(height: 8), Text( description, style: const TextStyle( fontSize: 14, height: 1.6, color: Color(0xFF5C6670), ), ), const SizedBox(height: 16), Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18), decoration: BoxDecoration( color: const Color(0xFFF7F9FC), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFD9E1F2)), ), child: child, ), const SizedBox(height: 14), Text( '当前结果:$resultText', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF4D6FD5), ), ), ], ), ), ); } String _formatDate(DateTime? date) { if (date == null) { return '未选择'; } return DateTimeUtils.dateTimeUtilsTool( dateTime: date.toIso8601String(), dateTimeUtilsType: DateTimeUtilsType.yearMonthDay, ); } String _formatDateWithWeekday(DateTime? date) { if (date == null) { return '未选择'; } return '${DateTimeUtils.dateTimeUtilsTool( dateTime: date.toIso8601String(), dateTimeUtilsType: DateTimeUtilsType.yearMonthDayWord, )} ${DateTimeUtils.getWeekDay(date)}'; } } class _InfoChip extends StatelessWidget { final String label; final String value; const _InfoChip({ required this.label, required this.value, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: const Color(0xFFF1F4FA), borderRadius: BorderRadius.circular(999), ), child: RichText( text: TextSpan( style: const TextStyle( fontSize: 12, color: Color(0xFF5C6670), ), children: [ TextSpan( text: '$label: ', style: const TextStyle(fontWeight: FontWeight.w700), ), TextSpan(text: value), ], ), ), ); } }