203 lines
5.3 KiB
Dart
203 lines
5.3 KiB
Dart
|
|
import 'dart:async';
|
|||
|
|
import 'dart:io';
|
|||
|
|
|
|||
|
|
import 'package:dio/dio.dart';
|
|||
|
|
import 'package:docx_viewer/docx_viewer.dart';
|
|||
|
|
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:path_provider/path_provider.dart';
|
|||
|
|
import 'package:pdfx/pdfx.dart';
|
|||
|
|
import 'package:qhd_prevention/pages/my_appbar.dart';
|
|||
|
|
|
|||
|
|
class ReadFilePage extends StatefulWidget {
|
|||
|
|
final String fileUrl;
|
|||
|
|
final int countdownSeconds;
|
|||
|
|
|
|||
|
|
const ReadFilePage({
|
|||
|
|
Key? key,
|
|||
|
|
required this.fileUrl,
|
|||
|
|
this.countdownSeconds = 3,
|
|||
|
|
}) : super(key: key);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
State<ReadFilePage> createState() => _ReadFilePageState();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class _ReadFilePageState extends State<ReadFilePage> {
|
|||
|
|
String? _localPath;
|
|||
|
|
bool _isLoading = true;
|
|||
|
|
bool _hasScrolledToBottom = false;
|
|||
|
|
bool _timerFinished = false;
|
|||
|
|
late int _secondsRemaining;
|
|||
|
|
Timer? _countdownTimer;
|
|||
|
|
PdfControllerPinch? _pdfController;
|
|||
|
|
int _totalPages = 0;
|
|||
|
|
bool _isDocxFile = false;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
void initState() {
|
|||
|
|
super.initState();
|
|||
|
|
_secondsRemaining = widget.countdownSeconds;
|
|||
|
|
_startCountdown();
|
|||
|
|
_prepareFile();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool _isDocxUrl(String url) {
|
|||
|
|
final path = Uri.parse(url).path.toLowerCase();
|
|||
|
|
return path.endsWith('.docx') || path.endsWith('.doc');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool _isPdfUrl(String url) {
|
|||
|
|
final path = Uri.parse(url).path.toLowerCase();
|
|||
|
|
return path.endsWith('.pdf');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Future<void> _prepareFile() async {
|
|||
|
|
final url = widget.fileUrl;
|
|||
|
|
|
|||
|
|
if (_isDocxUrl(url)) {
|
|||
|
|
if (!mounted) return;
|
|||
|
|
setState(() {
|
|||
|
|
_isDocxFile = true;
|
|||
|
|
_isLoading = false;
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!_isPdfUrl(url)) {
|
|||
|
|
if (!mounted) return;
|
|||
|
|
setState(() {
|
|||
|
|
_isLoading = false;
|
|||
|
|
});
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
const SnackBar(content: Text('目前仅支持 PDF、DOCX、DOC 文件预览')),
|
|||
|
|
);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await _downloadAndLoadPdf();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Future<void> _downloadAndLoadPdf() async {
|
|||
|
|
try {
|
|||
|
|
final url = widget.fileUrl;
|
|||
|
|
final uri = Uri.parse(url);
|
|||
|
|
final filename = uri.pathSegments.isNotEmpty ? uri.pathSegments.last : 'temp.pdf';
|
|||
|
|
|
|||
|
|
final dir = await getTemporaryDirectory();
|
|||
|
|
final filePath = '${dir.path}/$filename';
|
|||
|
|
|
|||
|
|
final dio = Dio();
|
|||
|
|
await dio.download(url, filePath);
|
|||
|
|
|
|||
|
|
if (!mounted) return;
|
|||
|
|
|
|||
|
|
_pdfController = PdfControllerPinch(
|
|||
|
|
document: PdfDocument.openFile(filePath),
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
setState(() {
|
|||
|
|
_localPath = filePath;
|
|||
|
|
_isLoading = false;
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
if (!mounted) return;
|
|||
|
|
|
|||
|
|
setState(() {
|
|||
|
|
_isLoading = false;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
SnackBar(content: Text('文件加载失败:$e')),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void _startCountdown() {
|
|||
|
|
_countdownTimer?.cancel();
|
|||
|
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|||
|
|
if (!mounted) return;
|
|||
|
|
|
|||
|
|
setState(() {
|
|||
|
|
if (_secondsRemaining > 1) {
|
|||
|
|
_secondsRemaining--;
|
|||
|
|
} else {
|
|||
|
|
_secondsRemaining = 0;
|
|||
|
|
_timerFinished = true;
|
|||
|
|
_countdownTimer?.cancel();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
void dispose() {
|
|||
|
|
_countdownTimer?.cancel();
|
|||
|
|
_pdfController?.dispose();
|
|||
|
|
super.dispose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
final isButtonEnabled = _timerFinished && _hasScrolledToBottom;
|
|||
|
|
|
|||
|
|
return Scaffold(
|
|||
|
|
appBar: MyAppbar(title: '文件详情'),
|
|||
|
|
backgroundColor: Colors.white,
|
|||
|
|
body: SafeArea(
|
|||
|
|
child: Column(
|
|||
|
|
children: [
|
|||
|
|
Expanded(
|
|||
|
|
child: _isLoading
|
|||
|
|
? const Center(child: CircularProgressIndicator())
|
|||
|
|
: _isDocxFile
|
|||
|
|
? DocxView(
|
|||
|
|
filePath: widget.fileUrl,
|
|||
|
|
fontSize: 16,
|
|||
|
|
onError: (error) {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
SnackBar(content: Text('DOCX 加载失败:$error')),
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
: _pdfController == null
|
|||
|
|
? const Center(
|
|||
|
|
child: Text('文件无法预览,请确认链接有效且为 PDF 文件'),
|
|||
|
|
)
|
|||
|
|
: PdfViewPinch(
|
|||
|
|
controller: _pdfController!,
|
|||
|
|
scrollDirection: Axis.vertical,
|
|||
|
|
onDocumentLoaded: (document) {
|
|||
|
|
setState(() {
|
|||
|
|
_totalPages = document.pagesCount;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
onPageChanged: (page) {
|
|||
|
|
if (_totalPages > 0 && page == _totalPages - 1) {
|
|||
|
|
setState(() => _hasScrolledToBottom = true);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
// Padding(
|
|||
|
|
// padding: const EdgeInsets.all(16),
|
|||
|
|
// child: ElevatedButton(
|
|||
|
|
// onPressed: isButtonEnabled
|
|||
|
|
// ? () {
|
|||
|
|
// Navigator.pop(context);
|
|||
|
|
// }
|
|||
|
|
// : null,
|
|||
|
|
// child: Text(
|
|||
|
|
// isButtonEnabled
|
|||
|
|
// ? '我已学习完毕'
|
|||
|
|
// : _secondsRemaining == 0
|
|||
|
|
// ? '请阅读完成'
|
|||
|
|
// : '($_secondsRemaining s)我已学习完毕',
|
|||
|
|
// ),
|
|||
|
|
// ),
|
|||
|
|
// ),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|