【问题标题】:How to get a Provider value in my Flutter view using Provider?如何使用 Provider 在我的 Flutter 视图中获取 Provider 值?
【发布时间】:2020-03-13 11:35:08
【问题描述】:

我正在按照this tutorial 中描述的模式使用 Provider (3.1.0) 中的 MultiProvider 构建一个颤振应用程序。

在我的主页中,我想加载配置数据(地点货币符号)但不显示模型。我在上面链接的教程中使用了类似于登录服务的模式,但是当我尝试在另一个视图(账单)中使用值 Provider.of<Venue>(context).currency 时,我得到了这个错误。

以下 NoSuchMethodError 被抛出构建 BillListItem(dirty, dependencies: [InheritedProvider]): getter 'currency' 在 null 上被调用。接收者:null 尝试调用: 货币

我无法弄清楚与我可以获取 Provider.of(context).name

的登录有什么不同

这是我的代码:

main.dart

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: providers,
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        initialRoute: RoutePaths.Login,
        onGenerateRoute: Router.generateRoute,
      ),
    );
  }
}

我要使用提供者值的视图。

class BillListItem extends StatelessWidget {
  final Bill bill;
  final Function onTap;

  const BillListItem({this.bill, this.onTap});
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        margin: EdgeInsets.symmetric(horizontal: 20.0, vertical: 15.0),
        padding: EdgeInsets.all(10.0),
        decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(5.0),
            boxShadow: [
              BoxShadow(
                  blurRadius: 3.0,
                  offset: Offset(0.0, 2.0),
                  color: Color.fromARGB(80, 0, 0, 0))
            ]),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(bill.billNumber.toString(), style: TextStyle(fontWeight: FontWeight.w900, fontSize: 16.0),),

            Text("${Provider.of<Venue>(context).currency}${bill.payable.toString()}", style: TextStyle(fontWeight: FontWeight.w900, fontSize: 16.0),),     
          ],
        ),
      ),
    );
  }  
}

我注入场地的标签视图:

class TabContainer extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => TabContainerState();
}

class TabContainerState extends State<TabContainer> {
  @override
  void initState(){
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _fetchVenue(context));
  }
  @override
  Widget build(BuildContext context) {
     return BaseWidget<VenueModel>(
      model: VenueModel(venueService: Provider.of(context)),
      builder: (context, model, child) => DefaultTabController(
      length: 2,       
      child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              isScrollable: false,
              tabs: [
                Tab(icon: Icon(Icons.home)),
                Tab(icon: Icon(Icons.room_service)),
              ],
            ), 
          centerTitle : true,
          title: Text('Exact POS'),
          actions: <Widget>[
             FutureBuilder<String>(
                future: SharedPreferencesHelper.getLanguageCode(),
                initialData: 'en',
                builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
                    return snapshot.hasData
                    ? BuildFlag.buildFlag(context, snapshot.data)
                    : Container();
                }
             ),
          ]
        ),

        body: SafeArea(
          child: TabBarView(
            children: [
              HomeView(),
              LoginView(),
            ],
          ),
        ),
       )
      )
    );
  }
   void _fetchVenue(BuildContext context) async {
     Api _api = new Api();
     VenueService _venueService = new VenueService(api: _api);
     VenueModel _venueModel = new VenueModel(venueService: _venueService); 
     var success = await _venueModel.fetchVenue();
  } 
}

home_view.dart。我怀疑这可能是我需要注入Venue 提供程序而不是TabContainer 的地方。

class HomeView extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => HomeViewState();
}

class HomeViewState extends State<HomeView> {
  @override
  void initState(){
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) => showSnackBar());
  }

  void showSnackBar(){
      Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Welcome ${Provider.of<User>(context).name}',    
                              style: snackBarStyle), 
                backgroundColor: snackBarColor,
                duration: Duration(seconds: 4),
      ));
  }

  @override 
  Widget build(BuildContext context) {
     return SafeArea(
               child: Scaffold(
                       backgroundColor: backgroundColor,
                       body: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: <Widget>[
                                  UIHelper.verticalSpaceSmall,
                                  Expanded(
                                     child: Tables(),)
                               ],
                       ),
             )
    );

  }  
}

我的视图模型VenueModel

class VenueModel extends BaseModel {
  VenueService _venueService;

  VenueModel(
      {@required VenueService venueService,}
  ) : _venueService = venueService;

  Future<bool> fetchVenue() async {
    setBusy(true);
    var success = await _venueService.fetchVenue();
    setBusy(false);
    return success;
  }
}

我的服务VenueService

class VenueService {
  final Api _api;
  VenueService({Api api}) : _api = api;

  StreamController<Venue> _venueController = StreamController<Venue>();
  Stream<Venue> get venue => _venueController.stream;

  Future<bool> fetchVenue() async {
    var fetchedVenue = await _api.getVenue();
    var hasVenue = fetchedVenue != null;
    if (hasVenue) {
      _venueController.add(fetchedVenue);
    }
    return hasVenue;
  }
}

我的BaseModel.dart

class BaseModel extends ChangeNotifier {
  bool _busy = false;
  bool get busy => _busy;

  void setBusy(bool value) {
    _busy = value;
    notifyListeners();
  }
}

我的提供者设置代码:

List<SingleChildCloneableWidget> providers = [
  ...independentServices,
  ...dependentServices,
  ...uiConsumableProviders
];

List<SingleChildCloneableWidget> independentServices = [ 
  Provider.value(value: Api())
];

List<SingleChildCloneableWidget> dependentServices = [
  ProxyProvider<Api, AuthenticationService>(
    builder: (context, api, authenticationService) => AuthenticationService(api: api),
  ),

  ProxyProvider<Api, VenueService>(
    builder: (context, api, venueService) => VenueService(api: api),
  ),
];

List<SingleChildCloneableWidget> uiConsumableProviders = [

  StreamProvider<User>(
    builder: (context) => Provider.of<AuthenticationService>(context, listen: false).user,  
  ),

    StreamProvider<Venue>(
    builder: (context) => Provider.of<VenueService>(context, listen: false).venue,
  ),
]; 

base_widget.dart

class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final T model;
  final Widget child;
  final Function(T) onModelReady;

  BaseWidget({
    Key key,
    this.builder,
    this.model,
    this.child,
    this.onModelReady,
  }) : super(key: key);

  _BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}

class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
  T model;

  @override
  void initState() {
    model = widget.model;

    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
      builder: (context) => model,
      child: Consumer<T>(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
}

【问题讨论】:

  • 你的MultiProvider在哪里?
  • 我编辑了我的答案以包含main.dart,其中包含MutiProvider
  • 你的BillListItem用在哪里?基于InheritedWidget的Providers,所以你必须确认BillListItem是TabContainer子元素
  • 你的VenueModel用在什么地方?只创建过一次吗?比如你的HomeView有这个代码VenueModel(venueService: Provider.of(context))
  • 是的,您可以在上面标题为主页视图的代码中看到它。

标签: flutter dart


【解决方案1】:

以下 NoSuchMethodError 被抛出构建 BillListItem(dirty, dependencies: [InheritedProvider]): getter 'currency' 在 null 上调用。接收方:null 尝试调用:货币

这个错误意味着Provider.of&lt;Venue&gt;(context) 正在返回null。这可能是由于两个原因:

  1. null 值被添加到VenueService 中的流中。鉴于您的代码,情况并非如此,这让我相信这是第二个原因:
  2. VenueService中没有流发出的值,所以流的初始数据是null

查看您的代码后,您在VenueService 中调用fetchVenue 的唯一时间是在您的标签视图中:

WidgetsBinding.instance.addPostFrameCallback((_) => _fetchVenue(context));
void _fetchVenue(BuildContext context) async {
  Api _api = new Api();
  VenueService _venueService = new VenueService(api: _api);
  VenueModel _venueModel = new VenueModel(venueService: _venueService); 
  var success = await _venueModel.fetchVenue();
} 

在您的_fetchVenue 函数中,您实际上是在创建ApiVenueServiceVenueModel 的新实例,这些实例不会在应用程序的其他任何地方使用。提供者列表中的实际VenueService 保持不变。实际VenueService 中的fetchVenue 函数永远不会被调用,这就是Provider.of&lt;Venue&gt;(context) 返回null 的原因,因为流没有值。

所以你可以用这个替换你的_fetchVenue函数:

void _fetchVenue(BuildContext context) async {
  VenueService _venueService = Provider.of(context);
  VenueModel _venueModel = new VenueModel(venueService: _venueService); 
  var success = await _venueModel.fetchVenue();
} 

还有一些改进需要做,比如_venueModel 没有在任何地方使用,但我希望这能解决你的问题!

【讨论】:

猜你喜欢
  • 2020-05-26
  • 2020-07-19
  • 2021-11-12
  • 1970-01-01
  • 2021-04-25
  • 2020-11-06
  • 2021-06-07
  • 2021-02-05
  • 2020-10-16
相关资源
最近更新 更多