Problem:
Can a widget request what they need without constructor injection or using singletons
https://pub.dev/packages/provider
A wrapper around InheritedWidget to make them easier to use and more reusable
add provider package to our project in pubspec.yaml
provider: ^4.3.3
Created a new class file📄 user_order_provider.dart under providers 📁 folder. We are moving all the logic that effects our usermodel here.
import 'package:burger_builder/models/dummy_data.dart';import 'package:burger_builder/models/user_order_model.dart';import 'package:flutter/foundation.dart';class UserOrderProvider extends ChangeNotifier {UserOrderModel _myuserOrderModel =UserOrderModel(customer: "sumith", userIngredients: [], totalPrice: 10);UserOrderModel get userOrderModel {return _myuserOrderModel;}bool get isEmptyIngredients {return _myuserOrderModel.userIngredients == null ||_myuserOrderModel.userIngredients.length == 0;}addIngredientHandler(String name) {var ingredient = dummyData.singleWhere((ing) => ing.name == name);final foundIngredient = _myuserOrderModel.userIngredients.singleWhere((element) => element.ingredient.name == name,orElse: () => null,);if (foundIngredient == null) {_myuserOrderModel.userIngredients.add(UserSelectedIngredientModel(ingredient: ingredient, count: 1),);} else {foundIngredient.count++;}_myuserOrderModel.totalPrice =_myuserOrderModel.totalPrice + ingredient.price;notifyListeners();}removeIngredientHandler(String name) {final ingredient = dummyData.singleWhere((ing) => ing.name == name);final foundIngredient = _myuserOrderModel.userIngredients.singleWhere((element) => element.ingredient.name == name,orElse: () => null,);if (foundIngredient != null) {foundIngredient.count--;}_myuserOrderModel.totalPrice =_myuserOrderModel.totalPrice - ingredient.price;_myuserOrderModel.userIngredients.removeWhere((element) => element.count == 0);notifyListeners();}void setDummyData() {_myuserOrderModel =UserOrderModel(customer: "sumith", userIngredients: [], totalPrice: 10);}}
Refactor file📄 main.dart ⇒ build method
return **ChangeNotifierProvider<UserOrderProvider>(create: (context) => UserOrderProvider(),**child: MaterialApp(debugShowCheckedModeBanner: false,title: 'Flutter Burger Builder',theme: ThemeData(primaryColor: AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR),visualDensity: VisualDensity.adaptivePlatformDensity,),home: Home(),),);
Start Removing dependency on userOrderModel from file📄 burger.dart
import 'package:burger_builder/models/user_order_model.dart';import 'package:burger_builder/providers/user_order_provider.dart';import 'package:burger_builder/widgets/burger_ingredient.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';class Burger extends StatefulWidget {**const Burger({Key key}) : super(key: key);**@override_BurgerState createState() => _BurgerState();}class _BurgerState extends State<Burger> {@overrideWidget build(BuildContext context) {return Container(child: Expanded(child: Padding(padding: const EdgeInsets.all(8.0),child: ListView(children: [Column(crossAxisAlignment: CrossAxisAlignment.center,children: [BurgerIngredient(type: "bread-top"),**if (Provider.of<UserOrderProvider>(context, listen: true).isEmptyIngredients)**EmptyIngredients(),...transformedIngredients,BurgerIngredient(type: "bread-bottom"),],),],),),),);}get transformedIngredients {**final myuserOrderModel =Provider.of<UserOrderProvider>(context).userOrderModel;**List<Widget> ingredientsList = [];for (var selectedIngredient in **myuserOrderModel.userIngredients**) {for (var i = 0; i < selectedIngredient.count; i++) {ingredientsList.add(BurgerIngredient(type: selectedIngredient.ingredient.name),);}}return ingredientsList;}}class EmptyIngredients extends StatelessWidget {const EmptyIngredients({Key key,}) : super(key: key);@overrideWidget build(BuildContext context) {return Container(child: Center(child: Text("Please start adding ingredients!",style: TextStyle(color: Colors.black,fontSize: 18.0,fontWeight: FontWeight.bold,),),),);}}
Remove dependency on userOrderModel from file📄 build_controls.dart
import 'package:burger_builder/helpers/app_constants.dart';import 'package:burger_builder/models/dummy_data.dart';import 'package:burger_builder/models/ingredients_model.dart';import 'package:burger_builder/models/user_order_model.dart';import 'package:burger_builder/providers/user_order_provider.dart';import 'package:burger_builder/screens/order_summary.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'build_control.dart';class BuildControls extends StatefulWidget {**BuildControls({Key key, this.ingredients}) : super(key: key);**final List<IngredientsModel> ingredients;@override_BuildControlsState createState() => _BuildControlsState();}class _BuildControlsState extends State<BuildControls> {@overrideWidget build(BuildContext context) {**final totalPrice = Provider.of<UserOrderProvider>(context, listen: true).userOrderModel.totalPrice;**return Container(color:AppConstants.hexToColor(AppConstants.BUILD_CONTROLS_CONTAINER_COLOR),child: Column(children: [Padding(padding: const EdgeInsets.only(top: 8.0, left: 8.0),child: Row(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Text('Current Price:',style: TextStyle(color: Colors.black,fontSize: 18.0,fontWeight: FontWeight.bold,),),SizedBox(width: 10),Text('\$${totalPrice.toStringAsFixed(2)}',style: TextStyle(color: Colors.black,fontSize: 15.0,fontWeight: FontWeight.bold,),),],),),buttonBar(),Align(alignment: Alignment.bottomCenter,child: RaisedButton(onPressed: totalPrice <= 0? null: () {showModalBottomSheet(context: context,builder: (context) => Container(padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom,),child: OrderSummary(),),);},child: const Text('ORDER NOW', style: TextStyle(fontSize: 20)),color:AppConstants.hexToColor(AppConstants.BUTTON_BACKGROUND_COLOR),textColor:AppConstants.hexToColor(AppConstants.BUTTON_TEXT_COLOR),elevation: 5,),)],),);}Widget buttonBar() {return Column(children: widget.ingredients.map<Widget>((ingredient) {**final userIngredient =****Provider.of<UserOrderProvider>(context, listen: true).userOrderModel?.userIngredients.singleWhere((ing) => ing.ingredient.name == ingredient.name,orElse: () => null);**final currentCount = userIngredient?.count ?? 0;return BuildControl(ingredient: ingredient,currentValue: currentCount,);}).toList(),);}}
Remove dependency on userOrderModel from file📄 build_control.dart , here we are adding refences to the functions in UserOrderProvider and passing that to custom stepper.
import 'package:burger_builder/models/ingredients_model.dart';import 'package:burger_builder/models/user_order_model.dart';import 'package:burger_builder/providers/user_order_provider.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'custom_stepper.dart';class BuildControl extends StatefulWidget {**BuildControl({Key key,@required this.ingredient,@required this.currentValue,}) : super(key: key);**final IngredientsModel ingredient;final int currentValue;@override_BuildControlState createState() => _BuildControlState();}class _BuildControlState extends State<BuildControl> {@overrideWidget build(BuildContext context) {return Container(child: Column(children: [Padding(padding: const EdgeInsets.only(left: 8.0),child: Row(children: [Row(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.baseline,children: [Text(widget.ingredient.label,style: TextStyle(color: Colors.black,fontSize: 18.0,fontWeight: FontWeight.bold,),),SizedBox(width: 10),Text("(\$" + "${widget.ingredient.price.toStringAsFixed(2)})",style: TextStyle(color: Colors.black,fontSize: 15.0,fontWeight: FontWeight.bold,),),],),Spacer(),CustomStepper(value: widget.currentValue,upperLimit: 5,lowerLimit: 0,stepValue: 1,iconSize: 25,name: widget.ingredient.name,**addHandler: () =>Provider.of<UserOrderProvider>(context, listen: false).addIngredientHandler(widget.ingredient.name),removeHandler: () =>Provider.of<UserOrderProvider>(context, listen: false).removeIngredientHandler(widget.ingredient.name)),**],),),],),);}}
Clean up file📄 custom_stepper.dart
.......RoundedIconButton(icon: Icons.remove,iconSize: widget.iconSize,onPress: widget.value == widget.lowerLimit? null: () {_setValue();widget.removeHandler();},),................RoundedIconButton(icon: Icons.add,iconSize: widget.iconSize,onPress: widget.value == widget.upperLimit? null: () {_setValue();widget.addHandler();},),
Lets now work on the file📄 order_summary.dart , notice we are using Consumer
import 'package:burger_builder/helpers/app_constants.dart';import 'package:burger_builder/providers/user_order_provider.dart';import 'package:burger_builder/services/http_service.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';class OrderSummary extends StatefulWidget {OrderSummary({Key key}) : super(key: key);@override_OrderSummaryState createState() => _OrderSummaryState();}class _OrderSummaryState extends State<OrderSummary> {bool visible = false;@overrideWidget build(BuildContext context) {**return Consumer<UserOrderProvider>(builder: (context, userOrderProvider, child) {**return Container(color: Color(0xff757575),child: Container(padding: EdgeInsets.all(20.0),decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.only(topLeft: Radius.circular(25.0),topRight: Radius.circular(25.0))),child: Column(crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Text('Your Order',textAlign: TextAlign.center,style: TextStyle(fontSize: 20.0,color: Colors.black,fontWeight: FontWeight.bold),),SizedBox(height: 5.0),Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text('A delicious burger with the following ingredients:',style: TextStyle(fontSize: 15.0, color: Colors.black),),],),Expanded(child: ListView.separated(itemCount:**userOrderProvider.userOrderModel.userIngredients.length,**separatorBuilder: (_, __) => Divider(height: 0.5),itemBuilder: (BuildContext context, int index) {var userIngredient =**userOrderProvider.userOrderModel.userIngredients**[index];return ListTile(leading: Icon(Icons.check_circle_outline_outlined,color: AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR),),title: Text('${userIngredient.ingredient.label} (${userIngredient.ingredient.price.toStringAsFixed(2)}) X ${userIngredient.count}',style: TextStyle(fontSize: 15.0, color: Colors.black),),trailing: Text('${(userIngredient.ingredient.price * userIngredient.count).toStringAsFixed(2)} ',style: TextStyle(fontSize: 15.0, color: Colors.black),),);},),),Text('Total Price : \$' +"${**userOrderProvider.userOrderModel.totalPrice.**toStringAsFixed(2)}",style: TextStyle(fontSize: 15.0,color: AppConstants.hexToColor(AppConstants.BUTTON_COLOR_CONTINUE,),fontWeight: FontWeight.bold),),Text('Continue to Chekout?',style: TextStyle(fontSize: 15.0, color: Colors.black),),Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [FlatButton(child: Text('CANCEL',style: TextStyle(color: Colors.white),),color: AppConstants.hexToColor(AppConstants.BUTTON_COLOR),onPressed: () => Navigator.pop(context),),visible? CircularProgressIndicator(backgroundColor: AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR,),): FlatButton(child: Text('CONTINUE',style: TextStyle(color: Colors.white),),color: AppConstants.hexToColor(AppConstants.BUTTON_COLOR_CONTINUE,),onPressed: () async {setState(() {visible = true;});var orderid = await HttpService().purchaseContinue(Provider.of<UserOrderProvider>(context,listen: false).userOrderModel);if (orderid.length > 0) {**Provider.of<UserOrderProvider>(context,listen: false).setDummyData();**SnackBar(behavior: SnackBarBehavior.floating,content: Text('order placed - ' + orderid),);}setState(() {visible = false;});Navigator.pop(context);},),],),],),),);});}}
Tip:- Now we can even change our stateful widgets to stateless widgets
Additional info:
https://pub.dev/packages/peanut
https://itsallwidgets.com/burger-buildr
https://itsallwidgets.com/submit/30days
https://imageresizer.com/ 1080x1920
Quick Links
Legal Stuff