In my opinion, learning a new language (fast and efficient learning) must be through practice, and the best way is to do projects. I will simply write a jingdong Demo here.

Set up the project framework on the first day and realize the functions of the home page: juejin.cn/editor/draf…

The next day to implement the classification and product list page: juejin.cn/post/704471…

The third day to realize the product details page function: juejin.cn/editor/draf…

Continuous Integration practices for Flutter- hybrid Engineering: juejin.cn/post/704209…

Before the realization of the home page, classification page, product list page and product details page function, this article to achieve the shopping cart page function.

Knowledge points used

1. Shared_preferences Implement local data storage

Shared_preferences is a key-value storage plugin that allows Flutter to persist data to disk. This plugin supports Android and iOS, and is based on NSUserDefaults in iOS. Based on SharedPreferences in Android.

Add a dependency to the project’s pubspec.yaml file: shared_preferences: Shared_preferences Support int, Double, bool, String, stringList.

Dart defines a storage.dart file in the services file that encapsulates common functions:

import 'package:shared_preferences/shared_preferences.dart'; Class Storage {// set value static Future<void> setString(key, value) async { SharedPreferences sp = await SharedPreferences.getInstance(); var result = sp.setString(key, value); } static Future<String? > getString(key) async{ SharedPreferences sp = await SharedPreferences.getInstance(); var result = sp.getString(key); return result; } / / remove the static Future < void > remove (key) async {SharedPreferences sp = await SharedPreferences. GetInstance (); sp.remove(key); } / / clean up the value of the static Future < void > the clear () is async {SharedPreferences sp = await SharedPreferences. GetInstance (); sp.clear(); }}Copy the code

I’m using the String example here to encapsulate a class dedicated to management. In previous articles, we talked about two ways to store data: juejin.cn/post/704098…

2. Turn the JSON Model

Serialization and deserialization of JSON is a common operation in daily development. If we parse JSON data manually, it is very troublesome. It saves a lot of work if you can do it automatically.

Tools to implement

On iOS I found a tool that automatically converts JSON data: juejin.cn/post/702689… . The conversion tool found in Flutter, app.quickType. IO, is also relatively useful.

This will enable transformation. Because Flutter disables runtime reflection, mature libraries such as MJExtension and YYModel are not fully parsed. Here we introduce a relatively mature library jSON_serializable that can be transformed.

Json_serializable implementation

Add dependencies to the project’s pubspec.yaml file:

Json_serializable: ^6.1.3 build_runner: ^2.1.7 jSON_annotation: ^4.4.0Copy the code

Then execute pub get. To use transformation, first of all need to use tools to generate a model class, tool address: caijinglong. Making. IO/json2dart/I…

Create a model class in your project and copy the tool transformation code into this model class

import 'package:json_annotation/json_annotation.dart';

part 'person.g.dart';


List<person> getpersonList(List<dynamic> list){
  List<person> result = [];
  list.forEach((item){
    result.add(person.fromJson(item));
  });
  return result;
}
@JsonSerializable()
class person extends Object with _$personSerializerMixin{

  @JsonKey(name: 'name')
  String name;

  @JsonKey(name: 'age')
  String age;

  @JsonKey(name: 'tele')
  String tele;

  person(this.name,this.age,this.tele,);

  factory person.fromJson(Map<String, dynamic> srcJson) => _$personFromJson(srcJson);

}
Copy the code

Then execute the flutter Packages pub run build_runner watch on the terminal to generate the person.g.art file in the project, which contains the transformed code.

The person.g.art file can also be generated by executing the flutter packages pub run build_runner build.

Note that when using the above tool, the data in the first map in the list will be parsed. If there are too many other map fields in the array, there will be fields missing. This should be checked carefully. It is not very convenient to use as a whole. It is better to use a tool to generate it directly: app.quickType. IO.

Plug-in JsonToDart implementation

Zhuanlan.zhihu.com/p/163330265 this plug-in can also realize the transformation

Install the JsonToDart plugin in Android Studio, open Preferences (Mac) or Setting (Window), select Plugins, and search for JsonToDart

Click Install to Install, and then restart. Select the directory, right click, and select New->Json to Dart, or use the shortcut key

Windows: ALT + Shift + D Mac: Option + Shift + DCopy the code

After selecting Json To Dart, a page is displayed To enter the Json data To be converted

Click Finish and the corresponding model file will be generated

This is the simplest of the three json to Model solutions.

The previous article implemented five JSON to Model solutions: juejin.cn/post/704701…

3. View UI effects on mobile phones with different resolutions

One of the biggest advantages of Flutter development is that it is cross-platform. When the Flutter development is complete, it will be difficult to view the effect on different mobile phones with different resolutions. Device_preview allows you to view UI effects on phones of different resolutions.

Configure Device_Preview: ^1.0.0, then execute pub get. Dart is used in main.dart

import 'package:device_preview/device_preview.dart'; void main() => runApp( DevicePreview( enabled: ! KReleaseMode,// Use Builder: (context) => MyApp(), // Wrap your app),); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( useInheritedMediaQuery: true, locale: DevicePreview.locale(context), builder: DevicePreview.appBuilder, theme: ThemeData.light(), darkTheme: ThemeData.dark(), home: const HomePage(), ); }}Copy the code

This package does the following:

  • Changing device Direction
  • Dynamic system configuration: language, dark mode, text scaling
  • Free to adjust the resolution and security area of the device
  • Keep the application state
  • screenshots

4. Provider status management

What is Provider state management?

When we want to share state (data) between multiple pages (components/widgets), or between multiple sub-components of a page (widgets/widgets), we can use state management in Flutter to manage uniform state (data). Realize direct value transfer and data sharing between different components. Provider is the state management mode introduced by the Official Flutter team.

Specific usage:

  • configurationThe provider: ^ the 6.0.1.
  • Create a new folder called Provider and put our state management classes in the Provider folder
  • Create a new cart.dart in the provider
  • Dart creates a new class inheritanceChangeNotifierThe code is as follows, and here we are dealing with the data in the shopping cart
class Cart with ChangeNotifier { List _cartList = []; Bool _isCheckAll = false; // double _allPrice = 0; // List get cartList => _cartList; bool get isCheckAll => _isCheckAll; double get allPrice => _allPrice; Cart(){ this.init(); } // get shopping cart data when initializing init() async {String? cartList = await Storage.getString(('cartList')); if(cartList ! = null){ List cartListData = json.decode(cartList); _cartList = cartListData; } else { _cartList = []; } _isCheckAll = this.ischeckAll; // Calculate the total price computeAllPrice(); notifyListeners(); } updateCartList() { this.init(); } itemCountChange() { Storage.setString('cartList', json.encode(_cartList)); // Calculate the total price computeAllPrice(); notifyListeners(); } // checkAll(value) {for (var I = 0; i < _cartList.length; i++) { _cartList[i]['checked'] = value; } _isCheckAll = value; // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); } bool isCheckedAll() {if (_cartList.length > 0) {for (var I = 0; i < cartList.length; i++) { if (_cartList[i]['checked'] == false) { return false; } } return true; } return false; } itemChage() {if (isCheckAll == true) {_isCheckAll = true; } else { _isCheckAll = false; } // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); } // computeAllPrice() {double tempAllPrice = 0; for (var i = 0; i < _cartList.length; i++) { if (_cartList[i]['checked'] == true) { tempAllPrice += _cartList[i]['price'] * _cartList[i]['count']; } } _allPrice = tempAllPrice; notifyListeners(); RemoveItem () {List tempList=[]; for (var i = 0; i < _cartList.length; i++) { if (_cartList[i]['checked'] == false) { tempList.add(_cartList[i]); } } _cartList=tempList; // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); }}Copy the code

Finally, don’t forget to add this file to the MultiProvider in main.dart

providers:[
  ChangeNotifierProvider(create: (_) => CheckOut()),
  ChangeNotifierProvider(create: (_) => Cart()),
],
Copy the code

Implementation effect

Concrete implementation code

Interface framework code

class CartPage extends StatefulWidget { CartPage({Key? key}) : super(key: key); _CartPageState createState() => _CartPageState(); } class _CartPageState extends State<CartPage> { bool _isEdit = false; var checkOutProvider; @override void initState() { super.initState(); } / / to settlement doCheckOut () async {/ / 1, to get a shopping cart the selected data List checkOutData = await CartServices. GetCheckOutData (); / / 2, save a shopping cart this. The selected data checkOutProvider. ChangeCheckOutListData (checkOutData); If (checkoutdata.length > 0) {navigator.pushnamed (context, '/checkOut'); ToastLength: ToastGravity: LENGTH_SHORT, gravity: ToastGravity.CENTER,); } } @override Widget build(BuildContext context) { var cartProvider = Provider.of<Cart>(context); checkOutProvider = Provider.of<CheckOut>(context); Return Scaffold(appBar: appBar (title: Text(' shopping cart '), Actions: [IconButton(onPressed: (){}, icon: Icon(Icons.launch)) ], ), body: cartProvider.cartList.length > 0 ? Stack(children: [// ListView(children: [Column(children: [Column(children: CartProvider. CartList. The map ((value) {/ / return to generate each Item return CartItem (value);}). ToList (),), SizedBox (height: Position (100))],)],), // the bottom of the whole and the clearing button would offer small tourists (bottom: 0, width: screenadapter.width (750), height: ScreenAdapter.height(78), child: Container( decoration: BoxDecoration( border: Border( top: BorderSide(width: 1, color: Colors.black12), ), color: Colors.white ), width: ScreenAdapter.width(750), height: ScreenAdapter.height(78), child: Stack( children: [ Align( alignment: Alignment.centerLeft, child: Row( children: [ Container( width: ScreenAdapter.width(60), child: Checkbox( value: false, activeColor: Colors.pink, onChanged: (v){},), Text(' all '),],),), Align(alignment: alignment. CenterRight, child: Container(margin: EdgeInsets. Only (right: 10), child: ElevatedButton(child: Text(' balance ', style: TextStyle(color: color.white),), style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.red), ), onPressed: () {doCheckOut ();},),),),),),,) : Center (child: Text (" of the shopping cart is empty..." ),),); }}Copy the code

The implementation code for each Item

Create a separate Cart folder and put the code extracted from the main page in it

class CartItem extends StatefulWidget { Map _itemData; CartItem(this._itemData,{Key? key}) : super(key: key); _CartItemState createState() => _CartItemState(); } class _CartItemState extends State<CartItem> {class _CartItemState extends State<CartItem> {class _CartItemState extends State<CartItem> { @override Widget build(BuildContext context) {// Note: Assign the attribute this._itemData=widget._itemData; Var cartProvider = provider.of <Cart>(context); return Container( height: ScreenAdapter.height(220), padding: EdgeInsets.all(5), decoration: BoxDecoration( border: Border(bottom: BorderSide(width: 1, color: Colors.black12))), child: Row( children: <Widget>[ Container( width: ScreenAdapter.width(60), child: Checkbox( value: _itemData["checked"], onChanged: (val) {_itemData["checked"]=!_itemData["checked"]; // Update cartProvider.itemchage ();}, activeColor: Colors.pink, ), ), Container( width: ScreenAdapter.width(160), child: Image.network( "${_itemData["pic"]}", fit: BoxFit.cover), ), Expanded( flex: 1, child: Container( padding: EdgeInsets.fromLTRB(10, 10, 10, 5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("${_itemData["title"]}", maxLines: 2), Text("${_itemData["selectedAttr"]}", maxLines: Align(Align: Align. CenterLeft, child: Text("¥${_itemData["price"]}",style: TextStyle(color: color.red), Align(alignment: align.centerright, // Increase/decrease the number of items child: CartNum(_itemData), ) ], ) ], ), ), ) ], ), ); }}Copy the code

Quantity per item plus/minus component code

class CartNum extends StatefulWidget { Map _itemData; CartNum(this._itemData,{Key? key}) : super(key: key); _CartNumState createState() => _CartNumState(); } class _CartNumState extends State<CartNum> { late Map _itemData; var cartProvider; @override Widget build(BuildContext context) {// notice _itemData=widget._itemData; cartProvider = Provider.of<Cart>(context); return Container( width: ScreenAdapter.width(168), decoration: BoxDecoration(border: Border.all(width: ScreenAdapter.width(2), color: Colors.black12)), child: Row( children: <Widget>[ _leftBtn(), _centerArea(), _rightBtn() ], ), ); } // Widget _leftBtn() {return InkWell(onTap: () {if(_itemData["count"]>1){_itemData["count"]--; cartProvider.itemCountChange(); } }, child: Container( alignment: Alignment.center, width: ScreenAdapter.width(45), height: ScreenAdapter.height(45), child: Text("-"), ), ); } // Widget _rightBtn() {return InkWell(onTap: (){_itemData["count"]++; cartProvider.itemCountChange(); }, child: Container( alignment: Alignment.center, width: ScreenAdapter.width(45), height: ScreenAdapter.height(45), child: Text("+"), ), ); } // Middle Widget _centerArea() {return Container(alignment: align.center, width: ScreenAdapter.width(70), decoration: BoxDecoration( border: Border( left: BorderSide(width: ScreenAdapter.width(2), color: Colors.black12), right: BorderSide(width: ScreenAdapter.width(2), color: Colors.black12), )), height: ScreenAdapter.height(45), child: Text("${_itemData["count"]}"), ); }}Copy the code

The provider code

Dart and check_out.dart are two files that are created to share data using a provider

class Cart with ChangeNotifier { List _cartList = []; Bool _isCheckAll = false; // double _allPrice = 0; // List get cartList => _cartList; bool get isCheckAll => _isCheckAll; double get allPrice => _allPrice; Cart(){ this.init(); } // get shopping cart data when initializing init() async {String? cartList = await Storage.getString(('cartList')); if(cartList ! = null){ List cartListData = json.decode(cartList); _cartList = cartListData; } else { _cartList = []; } _isCheckAll = this.ischeckAll; // Calculate the total price computeAllPrice(); notifyListeners(); } updateCartList() { this.init(); } itemCountChange() { Storage.setString('cartList', json.encode(_cartList)); // Calculate the total price computeAllPrice(); notifyListeners(); } // checkAll(value) {for (var I = 0; i < _cartList.length; i++) { _cartList[i]['checked'] = value; } _isCheckAll = value; // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); } bool isCheckedAll() {if (_cartList.length > 0) {for (var I = 0; i < cartList.length; i++) { if (_cartList[i]['checked'] == false) { return false; } } return true; } return false; } itemChage() {if (isCheckAll == true) {_isCheckAll = true; } else { _isCheckAll = false; } // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); } // computeAllPrice() {double tempAllPrice = 0; for (var i = 0; i < _cartList.length; i++) { if (_cartList[i]['checked'] == true) { tempAllPrice += _cartList[i]['price'] * _cartList[i]['count']; } } _allPrice = tempAllPrice; notifyListeners(); RemoveItem () {List tempList=[]; for (var i = 0; i < _cartList.length; i++) { if (_cartList[i]['checked'] == false) { tempList.add(_cartList[i]); } } _cartList=tempList; // Calculate the total price computeAllPrice(); Storage.setString('cartList', json.encode(_cartList)); notifyListeners(); }}Copy the code
class CheckOut with ChangeNotifier { List _checkOutListData = []; List get checkOutListData => _checkOutListData; changeCheckOutListData(data){ _checkOutListData=data; notifyListeners(); }}Copy the code

That’s the code for the shopping cart page.