如何使用提供程序在Flutter视图中获取提供程序值?

时间:2019-11-18 06:05:31

标签: flutter dart

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

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

  

以下NoSuchMethodError被抛出   BillListItem(脏,依赖项:[InheritedProvider]):   getter'currency'在null上被调用。接收方:null尝试调用:   货币

我不知道与获得Provider.of (上下文).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,
      ),
    );
  }
}

1 个答案:

答案 0 :(得分:1)

  

以下NoSuchMethodError被抛出   BillListItem(脏,依赖项:[InheritedProvider]):获取程序   在null上调用了“ currency”。接收方:null尝试通话:货币

此错误表示Provider.of<Venue>(context)返回null。这可能是由于两个原因:

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

浏览完代码后,只有在标签视图中才调用fetchVenue中的VenueService

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保持不变。实际的fetchVenue中的VenueService函数永远不会被调用,这就是Provider.of<Venue>(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并未在任何地方使用,但我希望这可以解决您的问题!