如何使我的 bloc 在一个可重用的小部件中工作?

huangapple go评论99阅读模式
英文:

How do i make my bloc work in a reusable widget created?

问题

你遇到的问题是因为GlassPlayerCard组件内部使用了BlocProvider.of<MusicControlBloc>(context)来获取MusicControlBloc,但是在GlassPlayerCard所在的上下文中找不到MusicControlBloc。为了解决这个问题,你可以尝试以下几种方法:

  1. BlocProvider<MusicControlBloc>包装在MyApp中:

    MyApp中包装BlocProvider,以便整个应用程序都可以访问MusicControlBloc。你已经在main.dart中使用MultiProvider包装MyApp,但似乎忽略了MusicControlBloc的提供。确保在providers列表中添加BlocProvider来提供MusicControlBloc

  2. GlassPlayerCardcontext中独立出来:

    如果GlassPlayerCard是在某个具有context的子部件中使用的,你可以尝试将GlassPlayerCard提升到更高层次的部件,以便它可以访问到MusicControlBloc。这可以通过在包含GlassPlayerCard的父部件中使用BlocProvider.of<MusicControlBloc>(context)来实现。

  3. 通过构造函数传递MusicControlBloc

    你可以尝试将MusicControlBloc作为GlassPlayerCard的构造函数参数传递,而不是从context中获取它。这样,你可以在创建GlassPlayerCard实例时将MusicControlBloc传递给它,而不必依赖context。确保在使用GlassPlayerCard的地方将MusicControlBloc传递给它。

以下是一些可能的解决方法,你可以根据你的项目结构和需求选择其中一个或多个来解决上述问题。希望这些建议有助于解决你的上下文错误问题。

英文:

I have a reusable widget I created below as part of a UI for my app
The code for the reusable widget is below

  1. // ignore_for_file: prefer_typing_uninitialized_variables
  2. import &#39;package:flutter/material.dart&#39;;
  3. import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
  4. import &#39;package:musica/musica/domain/entities/riverpod_file.dart&#39;;
  5. import &#39;package:musica/musica/presentation/manager/music_control_bloc.dart&#39;;
  6. import &#39;package:musica/musica/presentation/widgets/constants.dart&#39;;
  7. import &#39;package:musica/musica/presentation/widgets/reused_widgets/glassmorphism.dart&#39;;
  8. import &#39;package:provider/provider.dart&#39;;
  9. class GlassPlayerCard extends StatelessWidget {
  10. final String currentPlayingMusicTitle;
  11. final String musicArtist;
  12. final String imageLink;
  13. final MusicControlBloc? musicControlBloc;
  14. GlassPlayerCard(
  15. {required this.currentPlayingMusicTitle,
  16. required this.musicArtist,
  17. required this.imageLink,
  18. this.musicControlBloc})
  19. : super(key: UniqueKey());
  20. @override
  21. Widget build(BuildContext context) {
  22. final musicBloc = BlocProvider.of&lt;MusicControlBloc&gt;(context);
  23. final musicPlayerProvider = Provider.of&lt;MusicPlayerProvider&gt;(context);
  24. return GlassMorphicContainer(
  25. 500.0,
  26. 100.0,
  27. Row(
  28. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  29. children: [
  30. Expanded(
  31. child: Row(
  32. mainAxisAlignment: MainAxisAlignment.start,
  33. children: [
  34. addHorizontalSpacing(10),
  35. Column(
  36. children: [
  37. addVerticalSpacing(15),
  38. Padding(
  39. padding: const EdgeInsets.all(8.0),
  40. child: Container(
  41. decoration: BoxDecoration(
  42. borderRadius: BorderRadius.circular(20),
  43. border: Border.all(color: textColor, width: 2)),
  44. height: 60,
  45. width: 60,
  46. child: Image.network(
  47. imageLink,
  48. ),
  49. ),
  50. ),
  51. ],
  52. ),
  53. addHorizontalSpacing(10),
  54. Expanded(
  55. child: Column(
  56. mainAxisAlignment: MainAxisAlignment.center,
  57. children: [
  58. addVerticalSpacing(10),
  59. Text(currentPlayingMusicTitle,
  60. overflow: TextOverflow.ellipsis,
  61. style: mediumWhiteTextStyle),
  62. Text(musicArtist,
  63. style:
  64. smallWhiteTextStyle.copyWith(color: textColor)),
  65. ],
  66. ),
  67. ),
  68. ],
  69. ),
  70. ),
  71. Row(
  72. mainAxisAlignment: MainAxisAlignment.start,
  73. children: [
  74. // GestureDetector(
  75. // onTap: () {
  76. // musicPlayerProvider.pauseMusic();
  77. // },
  78. // child: I
  79. // con(Icons.pause, color: textColor)),
  80. addHorizontalSpacing(10),
  81. BlocBuilder&lt;MusicControlBloc, MusicControlState&gt;(
  82. builder: (context, state) {
  83. if (state is MusicControlInitialState) {
  84. return GestureDetector(
  85. onTap: () {
  86. musicBloc.add(MusicControlPlayEvent());
  87. },
  88. child: Icon(Icons.play_arrow, color: textColor));
  89. } else if (state is MusicControlLoadingState) {
  90. return const Center(
  91. child: CircularProgressIndicator(
  92. color: kDefaultIconDarkColor,
  93. value: 0.5,
  94. strokeWidth: 2,
  95. key: Key(&#39;musicLoading&#39;),
  96. ),
  97. );
  98. } else if (state is MusicControlPlayingState) {
  99. return GestureDetector(
  100. onTap: () {
  101. musicBloc.add(MusicControlPlayEvent());
  102. },
  103. child: Icon(Icons.pause, color: textColor));
  104. } else {
  105. return Container();
  106. }
  107. }),
  108. addHorizontalSpacing(10),
  109. Icon(Icons.skip_previous, color: textColor),
  110. addHorizontalSpacing(20),
  111. Icon(Icons.skip_next, color: textColor),
  112. addHorizontalSpacing(50),
  113. ],
  114. ),
  115. ],
  116. ));
  117. }
  118. }

I used it in my ui app as below

  1. import &#39;package:audioplayers/audioplayers.dart&#39;;
  2. import &#39;package:flutter/material.dart&#39;;
  3. import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
  4. import &#39;package:musica/generated/assets.dart&#39;;
  5. import &#39;package:musica/musica/domain/entities/riverpod_file.dart&#39;;
  6. import &#39;package:musica/musica/presentation/manager/music_control_bloc.dart&#39;;
  7. import &#39;package:musica/musica/presentation/widgets/constants.dart&#39;;
  8. import &#39;package:musica/musica/presentation/widgets/reused_widgets/animated_like_button.dart&#39;;
  9. import &#39;package:musica/musica/presentation/widgets/reused_widgets/custom_app_bar.dart&#39;;
  10. import &#39;package:musica/musica/presentation/widgets/reused_widgets/glass_player_card.dart&#39;;
  11. import &#39;package:musica/musica/presentation/widgets/reused_widgets/music_card_widget.dart&#39;;
  12. import &#39;package:provider/provider.dart&#39;;
  13. class CollectionDetailsWrapper extends StatelessWidget {
  14. const CollectionDetailsWrapper({Key? key}) : super(key: key);
  15. @override
  16. Widget build(BuildContext context) {
  17. return BlocProvider(
  18. create: (context) =&gt; MusicControlBloc(),
  19. );
  20. }
  21. }
  22. class CollectionDetailsPage extends StatefulWidget {
  23. final String imageUrl;
  24. final String title;
  25. final String artistName;
  26. final int fans;
  27. final String trackList;
  28. const CollectionDetailsPage(
  29. {Key? key,
  30. required this.imageUrl,
  31. required this.title,
  32. required this.artistName,
  33. required this.fans,
  34. required this.trackList})
  35. : super(key: key);
  36. @override
  37. State&lt;CollectionDetailsPage&gt; createState() =&gt; _CollectionDetailsPageState();
  38. }
  39. class _CollectionDetailsPageState extends State&lt;CollectionDetailsPage&gt; {
  40. MusicPlayerProvider musicPlayerProvider = MusicPlayerProvider();
  41. late Future trackListFuture;
  42. final audioPlayer = AudioPlayer();
  43. bool isPlaying = false;
  44. Duration position = const Duration(seconds: 0);
  45. Duration duration = const Duration(seconds: 0);
  46. String currentPlayingMusicTitle = &#39;&#39;;
  47. String currentPlayingMusicArtist = &#39;&#39;;
  48. String currentTrackLink = &#39;&#39;;
  49. @override
  50. void initState() {
  51. super.initState();
  52. trackListFuture = musicPlayerProvider.getTrackList(widget.trackList);
  53. audioPlayer.onPlayerStateChanged.listen((event) {
  54. setState(() {
  55. isPlaying = event == PlayerState.playing;
  56. });
  57. });
  58. audioPlayer.onDurationChanged.listen((newDuration) {
  59. setState(() {
  60. duration = newDuration;
  61. });
  62. });
  63. audioPlayer.onPositionChanged.listen((newPosition) {
  64. setState(() {
  65. position = newPosition;
  66. });
  67. });
  68. }
  69. @override
  70. Widget build(BuildContext context) {
  71. final musicProvider =
  72. Provider.of&lt;MusicPlayerProvider&gt;(context, listen: false);
  73. final height = MediaQuery.of(context).size.height;
  74. return Scaffold(
  75. bottomNavigationBar: GlassPlayerCard(
  76. imageLink: widget.imageUrl,
  77. currentPlayingMusicTitle: currentPlayingMusicTitle,
  78. musicArtist: currentPlayingMusicArtist,
  79. ),
  80. extendBodyBehindAppBar: true,
  81. extendBody: true,
  82. appBar: const PreferredSize(
  83. preferredSize: Size.fromHeight(50), child: CustomAppBar()),
  84. body: Column(
  85. children: [
  86. Stack(clipBehavior: Clip.none, children: &lt;Widget&gt;[
  87. Container(
  88. decoration: BoxDecoration(
  89. color: Colors.transparent,
  90. image: DecorationImage(
  91. fit: BoxFit.cover,
  92. image: NetworkImage(
  93. widget.imageUrl,
  94. ),
  95. ),
  96. ),
  97. height: height,
  98. ),
  99. Container(
  100. height: height,
  101. decoration: BoxDecoration(
  102. gradient: LinearGradient(
  103. begin: Alignment.bottomCenter,
  104. end: Alignment.topCenter,
  105. colors: [
  106. backgroundColor,
  107. Colors.transparent.withOpacity(0.5),
  108. ],
  109. stops: const [0.5, 0.9],
  110. )),
  111. ),
  112. Positioned.fill(
  113. child: ListView(
  114. scrollDirection: Axis.vertical,
  115. children: [
  116. Column(
  117. children: [
  118. Container(
  119. height: 234,
  120. width: 360,
  121. decoration: BoxDecoration(
  122. color: backgroundColor,
  123. borderRadius: BorderRadius.circular(20),
  124. image: DecorationImage(
  125. image: NetworkImage(widget.imageUrl),
  126. fit: BoxFit.cover,
  127. ),
  128. ),
  129. ),
  130. Padding(
  131. padding: const EdgeInsets.only(left: 20),
  132. child: Text(
  133. widget.title,
  134. style: const TextStyle(
  135. color: Color(0xffa4c7c6), fontSize: 25),
  136. ),
  137. ),
  138. addVerticalSpacing(10),
  139. Padding(
  140. padding: const EdgeInsets.only(left: 20),
  141. child: Text(
  142. widget.artistName,
  143. style: const TextStyle(
  144. color: Color(0xffa4c7c6), fontSize: 15),
  145. ),
  146. ),
  147. Padding(
  148. padding: const EdgeInsets.all(8.0),
  149. child: Row(
  150. mainAxisSize: MainAxisSize.min,
  151. children: [
  152. Expanded(
  153. child: Image.asset(
  154. Assets.iconsPlayAllIcon,
  155. height: 36,
  156. )),
  157. addHorizontalSpacing(5),
  158. Expanded(
  159. child: Image.asset(
  160. Assets.iconsAddToCollecIcon,
  161. height: 36,
  162. )),
  163. addHorizontalSpacing(5),
  164. const Expanded(
  165. child: AnimatedLikeButton(
  166. text: &quot;Like&quot;,
  167. animationPath:
  168. Assets.lottieAnimationsLike)),
  169. ],
  170. ),
  171. ),
  172. ],
  173. ),
  174. SizedBox(
  175. height: height - 510,
  176. child: FutureBuilder(
  177. future: trackListFuture,
  178. initialData: const Center(
  179. child: CircularProgressIndicator(
  180. color: Colors.white,
  181. backgroundColor: Colors.white,
  182. )),
  183. builder: (context, snapshot) {
  184. if (snapshot.hasError) {
  185. return const Center(
  186. child: Text(
  187. &#39;Something went wrong&#39;,
  188. style: mediumWhiteTextStyle,
  189. ));
  190. }
  191. if (snapshot.connectionState ==
  192. ConnectionState.waiting) {
  193. return Center(
  194. child: CircularProgressIndicator(
  195. color: textColor,
  196. ),
  197. );
  198. }
  199. return ListView.builder(
  200. shrinkWrap: true,
  201. physics: const ScrollPhysics(),
  202. itemCount: musicPlayerProvider.musicList.length,
  203. scrollDirection: Axis.vertical,
  204. itemBuilder: (context, index) {
  205. return MusicCardWidget(
  206. name: musicPlayerProvider.musicList[index].name,
  207. artist:
  208. musicPlayerProvider.musicList[index].artist,
  209. duration: musicPlayerProvider
  210. .musicList[index].duration,
  211. trackLink:
  212. musicPlayerProvider.musicList[index].link,
  213. onTapped: () {
  214. musicProvider.playMusic(musicPlayerProvider
  215. .musicList[index].link);
  216. setState(() {
  217. currentPlayingMusicArtist =
  218. musicPlayerProvider
  219. .musicList[index].artist;
  220. currentPlayingMusicTitle =
  221. musicPlayerProvider
  222. .musicList[index].name;
  223. currentTrackLink = musicPlayerProvider
  224. .musicList[index].link;
  225. });
  226. },
  227. );
  228. },
  229. );
  230. }),
  231. )
  232. ],
  233. ),
  234. )
  235. ]),
  236. ],
  237. ),
  238. );
  239. }
  240. }

Now the problem is I keep getting context errors

  1. The following assertion was thrown building GlassPlayerCard-[#fc2aa](dirty):
  2. BlocProvider.of() called with a context that does not contain a MusicControlBloc.
  3. No ancestor could be found starting from the context that was passed to BlocProvider.of&lt;MusicControlBloc&gt;().
  4. This can happen if the context you used comes from a widget above the BlocProvider.
  5. The context used was: GlassPlayerCard-[#fc2aa](dirty)

irrespective of where I place the bloc provider that provides the bloc to the rest of the app. Even wrapping my main MyApp function with the bloc provider doesn't work.

I tried to add the bloc as a constructor to the reusable widget GlassPlayerCard but it didnt solve the problem. Kindly assist, please.

This is my main.dart

  1. import &#39;package:flutter/material.dart&#39;;
  2. import &#39;package:musica/musica/domain/entities/riverpod_file.dart&#39;;
  3. import &#39;package:musica/musica/presentation/pages/screens/home.dart&#39;;
  4. import &#39;package:provider/provider.dart&#39;;
  5. void main() {
  6. runApp(MultiProvider(
  7. builder: (context, _) {
  8. return const MyApp();
  9. },
  10. providers: [
  11. ChangeNotifierProvider(create: (context) =&gt; MusicPlayerProvider()),
  12. ],
  13. ));
  14. }
  15. class MyApp extends StatelessWidget {
  16. const MyApp({Key? key}) : super(key: key);
  17. @override
  18. Widget build(BuildContext context) {
  19. return const MaterialApp(
  20. debugShowCheckedModeBanner: false,
  21. home: Home(),
  22. );
  23. }
  24. }

答案1

得分: 1

你的代码中实际上没有将 bloc 传递给 GlassPlayerCard。原因是你创建了一个 CollectionDetailsWrapper,其中包含一个 BlocProvider,但从你展示的代码片段中看,你实际上没有在任何地方使用它。你还需要传递一个 Widget 子属性,以便将详细信息页面添加为子元素。

确保你将 CollectionDetailsPage 包装在 CollectionDetailsWrapper 中,就像这样:

  1. // 你的新包装定义
  2. class CollectionDetailsWrapper extends StatelessWidget {
  3. final Widget child;
  4. const CollectionDetailsWrapper({Key? key, required this.child}) : super(key: key);
  5. @override
  6. Widget build(BuildContext context) {
  7. return BlocProvider(
  8. create: (context) => MusicControlBloc(),
  9. child: child,
  10. );
  11. }
  12. }
  13. /// 你在声明详细信息页面的任何地方的新实现
  14. ...
  15. CollectionDetailsWrapper(
  16. child: CollectionDetailsPage(...),
  17. ),
  18. ...

注意:以上只是代码的翻译部分,没有其他内容。

英文:

Nowhere in your code you are actually passing the bloc to the GlassPlayerCard. Reason is you created a CollectionDetailsWrapper That contains a BlocProvider, but you're not really using it anywhere (at least from the snippets you've shown). You also need to pass in a Widget child property so you can add the details page as the child

Make sure you are Wrapping your CollectionDetailsPage with your CollectionDetailsWrapper and that should do it. Like this:

  1. // Your new wrapper definition
  2. class CollectionDetailsWrapper extends StatelessWidget {
  3. final Widget child;
  4. const CollectionDetailsWrapper({Key? key, required this.child}) : super(key: key);
  5. @override
  6. Widget build(BuildContext context) {
  7. return BlocProvider(
  8. create: (context) =&gt; MusicControlBloc(),
  9. child: child,
  10. );
  11. }
  12. }
  13. /// your new implementation wherever you are declaring your details page
  14. ...
  15. CollectionDetailsWrapper(
  16. child: CollectionDetailsPage(...),
  17. ),
  18. ...

huangapple
  • 本文由 发表于 2023年2月23日 22:25:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75546130.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定