import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:momo/lib/context_menu_region.dart'; import 'package:momo/models/image_list_resp.dart'; import 'package:momo/models/image_resp.dart'; import 'package:momo/provider/rerender.dart'; import 'package:momo/request/http_client.dart'; /// A builder that includes an Offset to draw the context menu at. // typedef ContextMenuBuilder = Widget Function( // BuildContext context, Offset offset); class Gallery extends ConsumerWidget { const Gallery({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { UniqueKey key = ref.watch(uniqueIdProvider); return Container( color: Colors.white, child: ImageGrid( key: key, ), ); } } class ImageGrid extends StatefulWidget { const ImageGrid({Key? key}) : super(key: key); @override State createState() => _ImageGridState(); } class _ImageGridState extends State { int pageSize = 10; final PagingController _pagingController = PagingController(firstPageKey: 1); Future _fetchPage(int pageKey) async { try { final resp = await dio.get("/image/history", queryParameters: {"page": pageKey, "size": pageSize}); ImageListResp imageListResp = ImageListResp.fromJson(resp.data); final newItems = imageListResp.list; final isLastPage = !imageListResp.hasNext; if (isLastPage) { _pagingController.appendLastPage(newItems); } else { final nextPageKey = pageKey + 1; _pagingController.appendPage(newItems, nextPageKey); } } catch (error) { _pagingController.error = error; } } @override void initState() { // TODO: implement initState _pagingController.addPageRequestListener((pageKey) { _fetchPage(pageKey); }); super.initState(); } @override Widget build(BuildContext context) { return RefreshIndicator( child: PagedGridView( pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => ImageItem(image: item)), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: MediaQuery.of(context).size.width > 640 ? 5 : 3), ), onRefresh: () => Future.sync(() => _pagingController.refresh())); } } class ImageItem extends ConsumerStatefulWidget { const ImageItem({Key? key, required this.image}) : super(key: key); final ImageResp image; @override ConsumerState createState() => _ImageItemState(); } class _ImageItemState extends ConsumerState { // final ContextMenuController _contextMenuController = ContextMenuController(); @override void dispose() { // TODO: implement dispose // if (_contextMenuController.isShown) { // _contextMenuController.remove(); // } super.dispose(); } // void _showContextMenu(Offset position) { // _contextMenuController.show( // context: context, // contextMenuBuilder: (BuildContext context) { // return AdaptiveTextSelectionToolbar.buttonItems( // anchors: TextSelectionToolbarAnchors( // primaryAnchor: position, // ), // buttonItems: [ // ContextMenuButtonItem( // onPressed: () { // ContextMenuController.removeAny(); // showDialog( // context: context, // builder: (BuildContext context) => // AlertDialog( // title: const Text('删除图片'), // content: const Text("确认删除图片"), // actions: [ // TextButton( // onPressed: () => Navigator.pop(context, 'Cancel'), // child: const Text('Cancel'), // ), // TextButton( // onPressed: () { // Navigator.pop(context, 'OK'); // dio // .delete("/image/delete/${widget.image.id}") // .then((resp) { // ref // .read(uniqueIdProvider.notifier) // .updateId(); // // context.go("/"); // }); // }, // child: const Text('OK'), // ), // ], // )); // // _showDialog(context); // }, // label: '删除', // ), // ContextMenuButtonItem( // onPressed: () { // ContextMenuController.removeAny(); // // _showDialog(context); // Clipboard.setData(ClipboardData( // text: // "${dio.options.baseUrl}/image/${widget.image.file_path}")); // ScaffoldMessenger.of(context).showSnackBar(const SnackBar( // content: Row( // crossAxisAlignment: CrossAxisAlignment.center, // children: [ // Icon( // Icons.check_circle, // color: Colors.white, // ), // SizedBox( // width: 10, // ), // Text("已拷贝到剪贴板") // ], // ), // backgroundColor: Colors.lightGreen, // )); // }, // label: '复制链接', // ), // ContextMenuButtonItem( // onPressed: () { // ContextMenuController.removeAny(); // // _showDialog(context); // }, // label: '查看详情', // ), // ], // ); // }, // ); // } @override Widget build(BuildContext context) { return ContextMenuRegion( contextMenuBuilder: (BuildContext context, Offset offset) { return AdaptiveTextSelectionToolbar.buttonItems( anchors: TextSelectionToolbarAnchors( primaryAnchor: offset, ), buttonItems: [ ContextMenuButtonItem( onPressed: () { ContextMenuController.removeAny(); Clipboard.setData(ClipboardData( text: "https://raichi.hodokencho.com/api/image/${widget.image.file_path}")); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( Icons.check_circle, color: Colors.white, ), SizedBox( width: 10, ), Text("已拷贝到剪贴板") ], ), backgroundColor: Colors.lightGreen, )); }, label: '复制链接', ), ], ); }, child: GestureDetector( onTap: () { context.go(Uri( path: "/detail", queryParameters: {"id": "${widget.image.id}"}).toString()); }, child: Image.network( kIsWeb ? "/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512" : 'https://raichi.hodokencho.com/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512', fit: BoxFit.cover, errorBuilder: (BuildContext context, o, err) { return const Icon(Icons.error_outline); }, ), )); } }