在flutter中使用Provider,可以得到页面A和页面B的变化值吗?

问题描述 投票:0回答:1

我正在使用 Provider

provider: ^4.1.2

我有2页 。A、B页。

A页和B页显示了相同的简单设计,有以下代码。

PageA.dart

class PageA extends StatelessWidget {

   var songTitle = '';

   @override
   Widget build(BuildContext c) {
     return Column(children: <Widget>[
         FloatingActionButton.extended(
            onPressed: () {
                 // LOGIC : TRANSFER TO PAGE B AFTER CLICKED BUTTON FROM PAGE A
                 Navigator.push(context, MaterialPageRoute(
                     builder: (context) => PageB()));
            },
            label: Text('Click'),
           ),
         ),
         Text('$songTitle');
         // STEP 2 : while page B already got `songTitle` 
         // to show on UI, I want `songTitle` also update on this page A 
         // in the same time
     ]);
      
  }
}

PageB.dart

class PageB extends StatelessWidget {

   var songTitle = '';

   @override
   Widget build(BuildContext c) {
     return Text('$songTitle');
   }

   @override
   void initState() {
     super.initState();

     // LOGIC : CALL API TO GET `songTitle` TO UPDATE ON UI
     api.request().then((title) {
        setState(() {
           this.songTitle = title;
           // STEP 1 : this causes update to page B 
           // show `songTitle` got from api success
        });
     });
   }

}

这些代码在运行时没有错误。

我想要的是在 Step 1 得到 songTitle 数据。

它将更新其数据到A页(Step 2)和B页(Step 1)在同一时间内通过使用 Provider (前。Provider.of(context) ...)

知道的人。

请告诉我。

谢谢你了

ps:我不想使用这些方法。

- 使用 callback 作为参数,从A页发送至B页。

- 那么,就叫 callback() 后得 songTitle

甚至叫 Navigator.of(context).pop(songTitle) 在B页。

目前,我知道这些种类的 Provider 喜欢: ChangeNotifierProvider, StreamProvider,.v.v.

成功地用于包裹一个 StatelessWidget (在 build() 方法),通过这些组件 。

- ChangeNotifierProvider

- Model extends ChangeNotifiernotifyListener() 包括

- Provider<Model>.of(context)Consumer<Model>()

ps : 完整的示例代码。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider<Song>(create: (context) => Song())],
      child: MaterialApp(home: PageA()),
    );
  }
}

class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Consumer<Song>(builder: (context, song, child) {
          return Column(
            children: <Widget>[
              // SONG TITLE
              song != null ? Text(song.songTitle) : Text(''),
              // Button
              MaterialButton(
                onPressed: () => Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => PageB()),
                ),
                child: Text('Button'),
              ),
            ],
            mainAxisAlignment: MainAxisAlignment.center,
          );
        }),
      ),
    );
  }
}

class PageB extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return PageBState();
  }
}

class PageBState extends State<PageB> {
  @override
  Widget build(BuildContext c) {
    final song = Provider.of<Song>(c, listen: false);

    song.updateSongTitle('New Title');

    return Container(
      child: Text('Page B'),
      color: Colors.white,
    );
  }
}

class Song extends ChangeNotifier {
  String songTitle = 'Title';

  void updateSongTitle(String newTitle) {
    songTitle = newTitle;

    notifyListeners();
  }
}

当前的错误输出是 :

I/flutter (12496): ══╡ EXCEPTION CAUGHT BY 

FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
I/flutter (12496): The following assertion was thrown while dispatching notifications for Song:
I/flutter (12496): setState() or markNeedsBuild() called during build.
I/flutter (12496): This _InheritedProviderScope<Song> widget cannot be marked as needing to build because the framework
I/flutter (12496): is already in the process of building widgets.  A widget can be marked as needing to be built during
I/flutter (12496): the build phase only if one of its ancestors is currently building. This exception is allowed
I/flutter (12496): because the framework builds parent widgets before children, which means a dirty descendant will
I/flutter (12496): always be built. Otherwise, the framework might not visit this widget during this build phase.
I/flutter (12496): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (12496):   _InheritedProviderScope<Song>
I/flutter (12496): The widget which was currently being built when the offending call was made was:
I/flutter (12496):   PageB
I/flutter (12496): 
I/flutter (12496): When the exception was thrown, this was the stack:
I/flutter (12496): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4211:11)
...
I/flutter (12496): (elided 3 frames from dart:async)
I/flutter (12496): 
I/flutter (12496): The Song sending notification was:
I/flutter (12496):   Instance of 'Song'
I/flutter (12496): ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter dart flutter-provider
1个回答
1
投票

你读过Provider的例子吗?

  • 在flutter中,不要让你的widget处理逻辑操作,比如做API请求,你可以在你的provider中做,甚至更好的是创建一个独立的类,为你处理HTTP请求,然后你在你的provider代码中注入它。

你可以做这样的事情。

class Song{
  String title;

  Song(this.title);
}

class MyProvider extends ChangeNotifier{
   Song song;

   void getSongInformation(String song) async{
       try{
          final req = await doYourAPIRequest();
          song = Song(req['title']);

          notifyListeners(); // Notify the listeners about this change

       }catch(e){
          // handle the error
       }
   }
}

然后在你的小工具里

Consumer<Song>(
  builder: (context, song, child) {
    return Text(song.title);
  },
)

因此,任何正在监听歌曲变化的Widgets都会在你调用时收到任何变化的通知。notifyListeners()如果你只需要Provider来通知一个单一的Song变化,那么你也可以使用ValueNotifierProvider。另外,把你的Consumer放在Widget树的最深处,以便只重建依赖于该值的Widget,这是一个很好的做法。


1
投票

如果我没有理解错的话,你是想使用Provider包来管理你的状态,并将数据传递到你的Widget树中。

你需要使用ChangeNotifier和ChangeNotifierProvider来实现这个目标。

创建一个单独的类,像这样。

class Song extends ChangeNotifier {
     /// Here create a string for the songTitle you want to pass around
     /// You can also set a default value here if there are any
     String songTitle;

/// Then create a method that updates the songTitle from any widget that depends on this class

    void updateSongTitle(String newTitle) {
         songTitle = newTitle;
         notifyListeners();
    }
}

下一步是用ChangeNotifierProvider来包装你的MaterialApp,或者更好的是用MultiProvider来包装MaterialApp(这是在你需要用不同的provider来提供不同的widget的情况下),比如下面的main.dart。

return MultiProvider(
     provider: [
        /// Your ChangeNotifierProvider goes here like so
        ChangeNotifierProvider(
          create: (context) => Song(), /// This is the class that was created, don't forget to import it
        ),
     ],
     child: MaterialApp(
      /// Here should be familiar to you
     ),
)

对于B页: / 从你的代码中

class PageB extends StatefulWidget{ /// Remember StatelessWidget can't have initState

 @override
   void initState() {
     super.initState();

     // LOGIC : CALL API TO GET `songTitle` TO UPDATE ON UI
     api.request().then((title) {
        final song = Provider.of<Song>(context, listen: false);
        song.updateSongTitle(title); /// From the title String you get from your api call
     });
   }

   @override
   Widget build(BuildContext c) {
     /// Initialise another Provider for your Song class like so:
     /// A Consumer can also be used here, but I opted to use Provider.of(context)
     final song = Provider.of<Song>(context);
     return Text(song.songTitle);
   }

}

然后,对于你的页面A,从你的代码示例中可以看出

class PageA extends StatelessWidget {

   @override
   Widget build(BuildContext c) {
     return Column(children: <Widget>[
         FloatingActionButton.extended(
            onPressed: () {
                 // LOGIC : TRANSFER TO PAGE B AFTER CLICKED BUTTON FROM PAGE A
                 Navigator.push(context, MaterialPageRoute(
                     builder: (context) => PageB()));
            },
            label: Text('Click'),
           ),
         ),
     /// The text widget here gets its field via the Provider and its value is what was receive from Page B when where the api call was made to get the title
     Consumer<Song>(
            builder: (context, song, child) => Text(
              song.songTitle,
            ),
          ), /// Any other widget you have can continue from here
     ]);

  }
}

希望能成功!

© www.soinside.com 2019 - 2024. All rights reserved.