Files
Public/apps/native/lib/widgets/auth_image.dart
T
daiyongkang 76f266645d Initial commit: 风影 OA 全栈源码(API/Web/Flutter/IM)
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。
排除 node_modules、构建产物、安装包与 .env 密钥。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 10:04:03 +00:00

86 lines
1.9 KiB
Dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import '../api/oa_client.dart';
import '../theme.dart';
class AuthImage extends StatefulWidget {
const AuthImage({
super.key,
required this.api,
required this.fileId,
this.width,
this.height,
this.fit = BoxFit.cover,
this.placeholder,
});
final OaClient api;
final String fileId;
final double? width;
final double? height;
final BoxFit fit;
final Widget? placeholder;
@override
State<AuthImage> createState() => _AuthImageState();
}
class _AuthImageState extends State<AuthImage> {
Uint8List? _bytes;
bool _fail = false;
@override
void initState() {
super.initState();
_bytes = widget.api.peekFile(widget.fileId);
if (_bytes == null) _load();
}
@override
void didUpdateWidget(covariant AuthImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.fileId != widget.fileId) {
_bytes = widget.api.peekFile(widget.fileId);
_fail = false;
if (_bytes == null) _load();
}
}
Future<void> _load() async {
try {
final b = await widget.api.fileBytes(widget.fileId);
if (!mounted) return;
setState(() {
_bytes = b;
_fail = b == null;
});
} catch (_) {
if (mounted) setState(() => _fail = true);
}
}
@override
Widget build(BuildContext context) {
if (_bytes != null) {
return Image.memory(
_bytes!,
width: widget.width,
height: widget.height,
fit: widget.fit,
gaplessPlayback: true,
filterQuality: FilterQuality.medium,
);
}
return SizedBox(
width: widget.width,
height: widget.height,
child: widget.placeholder ??
ColoredBox(
color: const Color(0xFFEDEDED),
child: _fail ? const Icon(Icons.broken_image_outlined, color: kMute, size: 18) : const SizedBox.shrink(),
),
);
}
}