Flutter报错:'Future<dynamic>'不是'Widget'子类型的问题求助
问题:加载Firebase Storage中的PDF时出现类型错误
使用pdf_viewer插件从Firebase Storage加载并查看PDF文件时,遇到错误:type 'Future<dynamic>' is not a subtype of type 'Widget'。尝试用FutureBuilder包裹MaterialApp和WorkbookPage后错误依旧,无法定位问题方向。
相关代码
Main.dart
void main() { WidgetsFlutterBinding.ensureInitialized(); runApp( MultiProvider( providers: [ ChangeNotifierProvider( create: (context) => DrawingProvider(), ) ], child: MyApp() ) ); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final Future<FirebaseApp> _initialization = Firebase.initializeApp(); @override Widget build(BuildContext context) { return FutureBuilder<dynamic>( future: _initialization, builder: (context, snapshot) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData(primaryColor: Colors.white), home: WorkbookPage()); }, ); } } class WorkbookPage extends StatefulWidget { const WorkbookPage({Key? key}) : super(key: key); @override State<WorkbookPage> createState() => _WorkbookPageState(); } class _WorkbookPageState extends State<WorkbookPage> { GlobalKey _viewKey = GlobalKey(); bool isOverlayVisible = false; OverlayEntry? entry; @override Widget build(BuildContext context) { var p = Provider.of<DrawingProvider>(context); void hideOverlay() { entry?.remove(); entry = null; isOverlayVisible = false; } return GestureDetector( child: Scaffold( appBar: AppBar(title: Text('Workbook Test')), body: GestureDetector( onTap: () { hideOverlay(); }, child: Column( children: [ buildPenFunctionWidget(p, hideOverlay, context), Expanded( child: Stack( children: [ buildPDFViewer(), GestureDetector( child: Listener( behavior: HitTestBehavior.opaque, onPointerHover: (s) { if (s.buttons == kPrimaryStylusButton) { // print("Hovering stylus and button is pressed!"); } }, onPointerDown: (s) { if (s.kind == PointerDeviceKind.stylus) { p.penMode ? p.penDrawStart(s.localPosition) : null; p.highlighterMode ? p.highlighterDrawStart(s.localPosition) : null; p.eraseMode ? p.erase(s.localPosition) : null; } else if (s.buttons == kPrimaryStylusButton) { p.changeEraseModeButtonClicked = true; } }, onPointerMove: (s) { if (s.kind == PointerDeviceKind.stylus) { p.penMode ? p.penDrawing(s.localPosition) : null; p.highlighterMode ? p.highlighterDrawing(s.localPosition) : null; p.eraseMode ? p.erase(s.localPosition) : null; } else if (s.buttons == kPrimaryStylusButton) { p.changeEraseModeButtonClicked = true; } }, ), ), Positioned.fill( child: CustomPaint( painter: DrawingPainter(p.lines), ), ), ], ), ), ], ), )), ); } Container buildPenFunctionWidget(DrawingProvider p, void hideOverlay(), BuildContext context) { return Container( width: double.infinity, height: 50, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ penWidget(p: p), highlighterWidget(p: p), erasePenWidget(p: p), Container( key: _viewKey, width: 60, child: FittedBox( fit: BoxFit.fitWidth, child: GestureDetector( behavior: HitTestBehavior.deferToChild, child: const Icon( Icons.horizontal_rule_rounded, color: Colors.black54, ), onTap: () { if (isOverlayVisible) { hideOverlay(); } isOverlayVisible = true; OverlayState? overlayState = Overlay.of(context); final renderBox = _viewKey.currentContext ?.findRenderObject() as RenderBox; final offset = renderBox.localToGlobal(Offset.zero); entry = OverlayEntry( builder: (context) { return StatefulBuilder( builder: (context, setStateSB) { return Positioned( top: offset.dy + 60, left: offset.dx - 80, child: buildOverlaySliderWidget( p, setStateSB), ); }, ); }, ); overlayState?.insert(entry!); }, ), ), ), colorWidget(context: context, color: Colors.black), colorWidget(context: context, color: Colors.red), colorWidget(context: context, color: Colors.yellow), colorWidget(context: context, color: Colors.green), colorWidget(context: context, color: Colors.blue), ], ), ); } Material buildOverlaySliderWidget(DrawingProvider p, StateSetter setStateSB) { return Material( elevation: 2.5, child: Container( width: 250, child: FittedBox( fit: BoxFit.fill, child: Row( children: [ IconButton( onPressed: () { double penSize = p.penSize; if (penSize < 2) { penSize = 2; } p.changePenSize = --penSize; setStateSB(() { print("버튼 클릭 후 penSize = ${penSize}"); }); }, icon: FaIcon(FontAwesomeIcons.minus), ), Text("${p.penSize.toStringAsFixed(0)}"), Slider( inactiveColor: Colors.black26, activeColor: Colors.black, value: p.penSize, onChanged: (size) { setStateSB(() { p.changePenSize = size; }); }, min: 1, max: 15, ), IconButton( onPressed: () { double penSize = p.penSize; if (penSize > 14) { penSize = 14; } p.changePenSize = ++penSize; setStateSB(() { print("버튼 클릭 후 penSize = ${penSize}"); }); }, icon: Icon(Icons.add), ) ], ), ), ), ); } openPDF(BuildContext context, File file) { return Builder(builder: (context) => PdfViewer(file: file)); } buildPDFViewer() async { final url = '수특/2023_수능특강_수1.pdf'; final file = await PDFApi.loadFirebase(url); if (file == null) return; openPDF(context, file); } } class DrawingPainter extends CustomPainter { DrawingPainter(this.lines); final List<List<DotInfo>> lines; @override void paint(Canvas canvas, Size size) { for (var oneLine in lines) { Color? color; double? size; var path = Path(); var l = <Offset>[]; for (var oneDot in oneLine) { color ??= oneDot.color; size ??= oneDot.size; l.add(oneDot.offset); } path.addPolygon(l, false); canvas.drawPath( path, Paint() ..color = color! ..strokeWidth = size! ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke ..isAntiAlias = true ..strokeJoin = StrokeJoin.round); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
pdf_viewer.dart
class PdfViewer extends StatefulWidget { final File file; const PdfViewer({Key? key, required this.file}) : super(key: key); @override State<PdfViewer> createState() => _PdfViewerState(); } class _PdfViewerState extends State<PdfViewer> { late PDFViewController controller; int pages = 0; int indexPage = 0; @override Widget build(BuildContext context) { final name = basename(widget.file.path); final text = '${indexPage + 1} of $pages'; return Scaffold( body: PDFView( filePath: widget.file.path, swipeHorizontal: true, onRender: (pages) { setState(() { this.pages = pages!; }); }, onViewCreated: (controller) { setState(() { this.controller = controller; }); }, ), ); } }
错误日志
The following _TypeError was thrown building WorkbookPage(dirty, dependencies: [_InheritedProviderScope<DrawingProvider?>], state: _WorkbookPageState#a99ca): type 'Future<dynamic>' is not a subtype of type 'Widget' The relevant error-causing widget was: WorkbookPage WorkbookPage:file:///C:/Users/park/AndroidStudioProjects/stylus_test2/lib/main.dart:54:19
解决方案
问题根源
buildPDFViewer()是异步函数(带有async关键字),返回值类型为Future<dynamic>,但你直接将它放在Widget列表中,Flutter要求此处必须是Widget类型,导致类型不匹配错误。
修复步骤
- 修改
buildPDFViewer()的返回类型,确保它返回Future<Widget>,并直接返回PdfViewer组件(删除多余的openPDF函数):
Future<Widget> buildPDFViewer() async { final url = '수특/2023_수능특강_수1.pdf'; final file = await PDFApi.loadFirebase(url); // 加载失败时返回空组件,避免报错 if (file == null) return const SizedBox.shrink(); return PdfViewer(file: file); }
- 在State中存储Future对象,避免每次build方法执行时重新发起Firebase请求:
在_WorkbookPageState中添加变量并在initState初始化:
late Future<Widget> _pdfFuture; @override void initState() { super.initState(); _pdfFuture = buildPDFViewer(); }
- 用FutureBuilder包裹PDF加载逻辑,在Stack的children中替换原
buildPDFViewer():
Expanded( child: Stack( children: [ FutureBuilder<Widget>( future: _pdfFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 加载中显示进度指示器 return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { // 加载失败显示错误提示 return const Center(child: Text('PDF加载失败')); } else { // 返回PDF查看器或空组件 return snapshot.data ?? const SizedBox.shrink(); } }, ), // 其他原有组件(手势监听、CustomPaint等) ], ), ),
内容的提问来源于stack exchange,提问作者KRBILN
相关产品推荐
相关产品推荐

