Flutter开发AI音乐播放器遇初始化及late空值报错如何解决
Flutter AI音乐播放器空安全报错解决方案
报错原因
Dart 2.12及以上版本默认开启空安全校验,非可空类型的变量必须在声明时完成初始化,或在构造函数中赋值。你代码中第17、18、19行的radios、_selectedRadio、_selectedColor三个变量既没有初始化,也没有标记为可空,所以触发第一个初始化报错。
直接添加late关键字后,你是在异步方法fetchRadios中才给radios赋值,_selectedRadio要等用户触发播放操作后才会赋值,在赋值完成前访问这两个变量就会触发空值使用的崩溃。
解决办法
将未初始化的变量标记为可空类型,所有访问位置补充空判断,同时修复原有逻辑中未更新选中电台的隐藏bug。
具体修改点
- 变量声明段:将三个未初始化变量标记为可空,没用的
_selectedColor可以直接删除 - 双击卡片播放逻辑:调用现成的
playMusic方法,自动更新选中的电台 - 底部播放控件:补充空判断,未选中电台时默认播放第一个电台
修正后完整代码
import 'package:ai_music_player/model/radio.dart'; import 'package:ai_music_player/utils/ai_utils.dart'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:velocity_x/velocity_x.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { List<MyRadio>? radios; MyRadio? _selectedRadio; bool _isPlaying = false; final AudioPlayer _audioPlayer = AudioPlayer(); @override void initState() { super.initState(); fetchRadios(); _audioPlayer.onPlayerStateChanged.listen((event) { if (event == PlayerState.PLAYING) { _isPlaying = true; } else { _isPlaying = false; } setState(() {}); }); } fetchRadios() async { final radioJson = await rootBundle.loadString("assets/radio.json"); radios = MyRadioList.fromJson(radioJson).radios; setState(() {}); } playMusic(String url) { _audioPlayer.play(url); _selectedRadio = radios?.firstWhere((element) => element.url == url); print(_selectedRadio?.name ?? "未找到对应电台"); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( drawer: Drawer(), body: Stack( children: [ VxAnimatedBox() .size(context.screenWidth, context.screenHeight) .withGradient( LinearGradient( colors: [AIColors.primaryColor1, AIColors.primaryColor2], begin: Alignment.topLeft, end: Alignment.bottomRight), ) .make(), AppBar( title: "AI Player".text.xl4.bold.white.make().shimmer( primaryColor: Vx.purple300, secondaryColor: Colors.white), backgroundColor: Colors.transparent, elevation: 0.0, centerTitle: true, ).h(120), radios != null ? VxSwiper.builder( itemCount: radios!.length, enlargeCenterPage: true, itemBuilder: (context, index) { final rad = radios![index]; return VxBox( child: ZStack( [ Positioned( top: 0, right: 0, child: VxBox( child: rad.lang.text.uppercase.bold.white .make() .px16(), ) .height(40) .black .alignCenter .withRounded(value: 10) .make(), ), Align( alignment: Alignment.bottomCenter, child: VStack( [ rad.name.text.xl5.white.bold.make(), 5.heightBox, rad.tagline.text.xl.white.bold.italic.make(), ], crossAlignment: CrossAxisAlignment.center, ), ), Align( alignment: Alignment.center, child: [ const Icon( CupertinoIcons.play_circle, color: Colors.white54, size: 60, ), 10.heightBox, "DOUBLE TAB TO PLAY".text.gray300.make() ].vStack(), ), ], ), ) .clip(Clip.antiAlias) .bgImage( DecorationImage( image: NetworkImage(rad.image), fit: BoxFit.cover, colorFilter: ColorFilter.mode( Colors.black.withOpacity(0.3), BlendMode.darken), ), ) .border(color: Colors.black, width: 3) .withRounded(value: 60.0) .make() .onInkDoubleTap(() { playMusic(rad.url); }); }, aspectRatio: 1.0, ).centered() : const Center( child: CircularProgressIndicator( color: Colors.white, ), ), Align( alignment: Alignment.bottomCenter, child: [ if (_isPlaying && _selectedRadio != null) "Playing - ${_selectedRadio!.name} FM".text.makeCentered(), Icon( _isPlaying ? CupertinoIcons.stop_circle : CupertinoIcons.play_circle, color: Colors.white, size: 60, ).onInkTap(() { if (_isPlaying) { _audioPlayer.stop(); } else { if (_selectedRadio != null) { _audioPlayer.play(_selectedRadio!.url); } else if (radios?.isNotEmpty == true) { playMusic(radios!.first.url); } } }) ].vStack(), ).pOnly(bottom: context.percentHeight * 12) ], fit: StackFit.expand, ), ); } }
内容的提问来源于stack exchange,提问作者Rudransh Singh Mahra
相关产品推荐
相关产品推荐

