app/lib/widgets/widgets.dart

208 lines
6.0 KiB
Dart
Raw Normal View History

2024-03-07 23:01:32 +08:00
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
2024-03-09 20:22:44 +08:00
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
2024-03-07 23:01:32 +08:00
import 'package:url_launcher/url_launcher_string.dart';
import 'package:window_manager/window_manager.dart';
2023-10-09 09:32:07 +08:00
import 'package:markdown_widget/config/all.dart';
import 'package:markdown_widget/widget/all.dart';
2024-03-07 23:01:32 +08:00
import 'package:extended_image/extended_image.dart';
2024-03-09 21:53:37 +08:00
import 'dart:ui' as ui;
export 'src/cache_image.dart';
export 'src/countdown_time_text.dart';
export '../common/utils/async.dart';
2024-03-09 21:53:37 +08:00
export '../common/utils/base_utils.dart';
Widget makeLoading(
BuildContext context, {
double? width,
}) {
width ??= 30;
return Center(
child: SizedBox(
width: width,
height: width,
child: const ProgressRing(),
),
);
}
2023-10-09 09:32:07 +08:00
2024-03-07 23:01:32 +08:00
Widget makeDefaultPage(BuildContext context,
{Widget? titleRow,
List<Widget>? actions,
Widget? content,
bool automaticallyImplyLeading = true,
2024-03-10 14:18:30 +08:00
String title = "",
bool useBodyContainer = false}) {
2024-03-07 23:01:32 +08:00
return NavigationView(
appBar: NavigationAppBar(
automaticallyImplyLeading: automaticallyImplyLeading,
title: DragToMoveArea(
child: titleRow ??
Column(
children: [
Expanded(
child: Row(
children: [
Text(title),
],
),
)
],
),
2023-10-09 09:32:07 +08:00
),
2024-03-07 23:01:32 +08:00
actions: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [...?actions, const WindowButtons()],
)),
2024-03-10 14:18:30 +08:00
content: useBodyContainer
? Container(
decoration: BoxDecoration(
color: FluentTheme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(9),
),
child: content,
)
: content,
2024-03-07 23:01:32 +08:00
);
2023-10-09 09:32:07 +08:00
}
2024-03-07 23:01:32 +08:00
class WindowButtons extends StatelessWidget {
const WindowButtons({super.key});
2023-10-09 09:32:07 +08:00
2024-03-07 23:01:32 +08:00
@override
Widget build(BuildContext context) {
final FluentThemeData theme = FluentTheme.of(context);
return SizedBox(
width: 138,
height: 50,
child: WindowCaption(
brightness: theme.brightness,
backgroundColor: Colors.transparent,
),
);
}
2023-10-09 09:32:07 +08:00
}
2023-12-05 21:26:46 +08:00
List<Widget> makeMarkdownView(String description, {String? attachmentsUrl}) {
2023-10-28 13:00:10 +08:00
return MarkdownGenerator().buildWidgets(description,
2023-10-09 09:32:07 +08:00
config: MarkdownConfig(configs: [
2023-10-28 13:00:10 +08:00
LinkConfig(onTap: (url) {
2023-12-05 21:26:46 +08:00
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
2023-10-09 09:32:07 +08:00
}
2023-10-28 13:00:10 +08:00
launchUrlString(url);
}),
ImgConfig(builder: (String url, Map<String, String> attributes) {
2023-12-05 21:26:46 +08:00
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
}
2023-10-28 13:00:10 +08:00
return ExtendedImage.network(
url,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: [
ProgressRing(),
SizedBox(
height: 12,
),
Text("加载图片...")
],
),
),
);
case LoadState.completed:
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
);
case LoadState.failed:
2023-12-05 21:26:46 +08:00
return Text("Loading Image error $url");
2023-10-28 13:00:10 +08:00
}
},
);
})
]));
2023-10-09 09:32:07 +08:00
}
2024-03-09 20:22:44 +08:00
2024-03-09 21:53:37 +08:00
ColorFilter makeSvgColor(Color color) {
return ui.ColorFilter.mode(color, ui.BlendMode.srcIn);
}
2024-03-09 20:22:44 +08:00
CustomTransitionPage<T> myPageBuilder<T>(
BuildContext context, GoRouterState state, Widget child) {
return CustomTransitionPage(
child: child,
2024-03-10 14:18:30 +08:00
transitionDuration: const Duration(milliseconds: 150),
reverseTransitionDuration: const Duration(milliseconds: 150),
2024-03-09 20:22:44 +08:00
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
2024-03-10 14:18:30 +08:00
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0),
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeInOut,
)),
child: child,
2024-03-09 20:22:44 +08:00
);
});
}
class LoadingWidget<T> extends HookConsumerWidget {
final T? data;
final Future<T?> Function()? onLoadData;
final Widget Function(BuildContext context, T data) childBuilder;
const LoadingWidget(
{super.key, this.data, required this.childBuilder, this.onLoadData});
@override
Widget build(BuildContext context, WidgetRef ref) {
final dataState = useState<T?>(null);
final errorMsg = useState("");
useEffect(() {
if (data == null && onLoadData != null) {
_loadData(dataState, errorMsg);
return null;
}
return null;
}, const []);
if (errorMsg.value.isNotEmpty) {
return Button(
onPressed: () {
_loadData(dataState, errorMsg);
},
child: Center(
child: Text(errorMsg.value),
),
);
}
if (dataState.value == null && data == null) return makeLoading(context);
return childBuilder(context, (data ?? dataState.value) as T);
}
void _loadData(
ValueNotifier<T?> dataState, ValueNotifier<String> errorMsg) async {
errorMsg.value = "";
try {
final r = await onLoadData!();
dataState.value = r;
} catch (e) {
errorMsg.value = e.toString();
}
}
}
addPostFrameCallback(Function() callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}