import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class GraduallyPrintText extends StatefulWidget { const GraduallyPrintText({ super.key, this.initTxt, required this.graduallyController, this.textStyle, }); final TextStyle? textStyle; final GraduallyController graduallyController; final String? initTxt; @override State createState() => _GraduallyPrintTextState(); } typedef GraduallyTxtListener = void Function(String txt); class GraduallyController { String graduallyTxt = ''; int progressIndex = 0; Timer? appendTimer; bool? isAppendDone; bool? isShowDone; Duration printSpeed; GraduallyTxtListener? listener; VoidCallback? finishedListener; GraduallyController({this.printSpeed = const Duration(milliseconds: 60)}); setGraduallyTxtListener(GraduallyTxtListener listener) { this.listener = listener; } setGraduallyFinishedListener(VoidCallback finishedListener) { this.finishedListener = finishedListener; } void clear() { appendTimer?.cancel(); appendTimer = null; graduallyTxt = ''; progressIndex = 0; isAppendDone = null; isShowDone = null; listener?.call(''); // 通知 UI 清空 } append(String txt) { graduallyTxt += txt; _startAppend(); } void _initAppend() { progressIndex = 0; isAppendDone = null; isShowDone = null; appendTimer = null; } appendDone() { isAppendDone = true; // debugPrint('GraduallyMdText: appendDone'); } _startAppend() { if (appendTimer != null) { return; } _initAppend(); appendTimer = Timer.periodic(printSpeed, (timer) { // debugPrint('GraduallyMdText-progressIndex: $progressIndex'); if (progressIndex < graduallyTxt.length) { progressIndex += 1; listener?.call(graduallyTxt.substring(0, progressIndex)); } else if (isAppendDone == true) { // debugPrint('GraduallyMdText: appendTimer done'); dispose(); finishedListener?.call(); } }); } dispose() { appendTimer?.cancel(); isAppendDone = null; isShowDone = true; appendTimer = null; } } class _GraduallyPrintTextState extends State { String mdTxt = ''; @override void initState() { super.initState(); mdTxt = widget.initTxt ?? ''; widget.graduallyController.setGraduallyTxtListener((txt) { if (mounted) { setState(() { mdTxt = txt; }); } }); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return SelectionArea( child: Text( mdTxt, style: widget.textStyle, ), ); } }