简体   繁体   中英

Flutter CRUD model with provider

I'm learning Flutter with Provider and trying to make a CRUD model. I've achieved the create part, read and the delete part, but struggling with the update part of the model. I made and API call that reads and displays the data, made a method in which a new object is created and one for removing the object, but all is left is modifying/edit the object. Here is the code I've done so far:

The Vehicle list that calls the API:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/vehicle.dart';
import 'screens/vehicle_details_screen.dart';
import 'services/vehicle_api.dart';
import 'models/vehicle_data_provider.dart';

class VehicleList extends StatefulWidget {
  @override
  _VehicleList createState() => _VehicleList();
}

class _VehicleList extends State<VehicleList> {

  _getPosts() async {
    var provider = Provider.of<HomePageProvider>(context, listen: false);

    var postsResponse = await fetchVehicles();
    if (postsResponse.isSuccessful) {
      provider.setPostsList(postsResponse.data, notify: false);
    } else {
      provider.mergePostsList(postsResponse.data, notify: false);
    }

    provider.setIsHomePageProcessing(false);
  }

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

    _getPosts();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Consumer<HomePageProvider>(
          builder: (context, vehicleData, child) {
            return Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                SizedBox(
                  height: 12.0,
                ),
                Container(
                  decoration: BoxDecoration(
                    color: Colors.grey[300],
                    borderRadius: BorderRadius.all(
                      Radius.circular(12.0),
                    ),
                  ),
                  child: SingleChildScrollView(
                    child: DataTable(
                      columnSpacing: 50,
                      columns: <DataColumn>[
                        DataColumn(
                          label: Text(
                            'Friendly Name',
                            style: TextStyle(fontStyle: FontStyle.italic),
                          ),
                        ),
                        DataColumn(
                          label: Text(
                            'Licence Plate',
                            style: TextStyle(fontStyle: FontStyle.italic),
                          ),
                        ),
                        DataColumn(
                          label: Text(
                            'Delete',
                            style: TextStyle(fontStyle: FontStyle.italic),
                          ),
                        ),
                      ],
                      rows: List.generate(
                        vehicleData.postsList.length,
                        (index) {
                          VehicleData post = vehicleData.getPostByIndex(index);
                          return DataRow(
                            cells: <DataCell>[
                              DataCell(
                                Text('${post.friendlyName}'),
                                onTap: () {
                                  Navigator.push(
                                      context,
                                      MaterialPageRoute(
                                          builder: (context) =>
                                              VehicleDetailsScreen(
                                                color: post.color,
                                                friendlyName: post.friendlyName,
                                                licencePlate: post.licencePlate,
                                              )));
                                },
                              ),
                              DataCell(
                                Text('${post.licencePlate}'),
                              ),
                              DataCell(
                                IconButton(
                                  icon: Icon(Icons.delete),
                                  onPressed: () {
                                    vehicleData.deletePost(post);
                                  },
                                ),
                                // onTap: vehicleData.deletePost(post),
                              ),
                            ],
                          );
                        },
                      ),
                    ),
                  ),
                ),
              ],
            );
          },
        ),
      ],
    );
  }
}

The vehicle details screen

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/vehicle.dart';
import 'models/vehicle_data_provider.dart';

class VehicleDetailsScreen extends StatefulWidget {
  static const String id = 'vehicle_details_screen';
  final vehicleList;
  String color, friendlyName, licencePlate;
  VehicleDetailsScreen(
      {this.vehicleList, this.color, this.friendlyName, this.licencePlate});
  @override
  _VehicleDetailsScreenState createState() => _VehicleDetailsScreenState();
}

class _VehicleDetailsScreenState extends State<VehicleDetailsScreen> {
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
  String color, friendlyName, licencePlate;
  final TextEditingController _controllerfriendlyName = TextEditingController();
  final TextEditingController _controllerlicencePlate = TextEditingController();
  final TextEditingController _controllerColor = TextEditingController();

  @override
  void initState() {
    super.initState();
    setState(() {
      _controllerColor.text = widget.color;
      _controllerfriendlyName.text = widget.friendlyName;
      _controllerlicencePlate.text = widget.licencePlate;
    });
  }

  @override
  void dispose() {
    _controllerColor.dispose();
    _controllerfriendlyName.dispose();
    _controllerlicencePlate.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final myProvider = Provider.of<HomePageProvider>(context);
    return Scaffold(
      body: Container(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        color: Colors.white,
        child: Container(
          padding: EdgeInsets.all(20.0),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20.0),
              topRight: Radius.circular(20.0),
            ),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'Vehicle Details',
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 30.0,
                  color: Colors.grey,
                ),
              ),
              Container(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height - 400,
                child: Form(
                  key: _formKey,
                  child: new ListView(
                    shrinkWrap: true,
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    children: <Widget>[
                      TextFormField(
                        validator: (value) {
                          if (value.isEmpty) {
                            return 'Please enter friendly name';
                          }
                          return null;
                        },
                        controller: _controllerColor,
                        onSaved: (value) {
                          color = value;
                        },
                        decoration: const InputDecoration(
                          icon: Icon(Icons.directions_car),
                          hintText: 'Enter friendly car name',
                          labelText: 'Name',
                        ),
                        keyboardType: TextInputType.datetime,
                      ),
                      TextFormField(
                        onSaved: (value) {
                          friendlyName = value;
                        },
                        controller: _controllerfriendlyName,
                        validator: (value) {
                          if (value.isEmpty) {
                            return 'Please enter vehicle model';
                          }
                          return null;
                        },
                        decoration: const InputDecoration(
                          icon: Icon(Icons.directions_car_outlined),
                          hintText: 'Enter vehicle model',
                          labelText: 'Model',
                        ),
                        keyboardType: TextInputType.phone,
                      ),
                      TextFormField(
                        validator: (value) {
                          if (value.isEmpty) {
                            return 'Please enter some text';
                          }
                          return null;
                        },
                        controller: _controllerlicencePlate,
                        onSaved: (value) {
                          licencePlate = value;
                        },
                        decoration: const InputDecoration(
                          icon: Icon(Icons.calendar_today),
                          hintText: 'Enter manufactured year',
                          labelText: 'Year',
                        ),
                        keyboardType: TextInputType.emailAddress,
                      ),
                    ],
                  ),
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  FlatButton(
                    child: Text(
                      'Cancel',
                      style: TextStyle(
                        color: Colors.white,
                      ),
                    ),
                    color: Colors.grey,
                    onPressed: () {
                      Navigator.pop(context);
                    },
                  ),
                  SizedBox(
                    width: 10,
                  ),
                  FlatButton(
                    child: Text(
                      'Edit',
                      style: TextStyle(
                        color: Colors.white,
                      ),
                    ),
                    color: Colors.grey,
                    onPressed: () async {
                      if (_formKey.currentState.validate()) {
                        _formKey.currentState.save();
////The method bellow updateList ()needs to be changed in order to modify the existing list, this method just creates a new one.
                        await myProvider.updateList(VehicleData(  
                          friendlyName: friendlyName.toString(),
                          color: color.toString(),
                          licencePlate: licencePlate.toString(),
                        ));
                        Navigator.of(context).pop();
                      }
                    },
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

And here is the HomePageProvider model:

import 'package:flutter/foundation.dart';
import 'models/vehicle.dart';

class HomePageProvider extends ChangeNotifier {
  bool _isHomePageProcessing = true;
  int _currentPage = 1;
  String friendlyName;
  List<VehicleData> _postsList = [];
  bool _shouldRefresh = true;

  bool get shouldRefresh => _shouldRefresh;

  setShouldRefresh(bool value) => _shouldRefresh = value;

  int get currentPage => _currentPage;

  setCurrentPage(int page) {
    _currentPage = page;
  }

  bool get isHomePageProcessing => _isHomePageProcessing;

  setIsHomePageProcessing(bool value) {
    _isHomePageProcessing = value;
    notifyListeners();
  }

  List<VehicleData> get postsList => _postsList;

  setPostsList(List<VehicleData> list, {bool notify = true}) {
    _postsList = list;
    if (notify) notifyListeners();
  }
// This method needs to be changed
  updateList(VehicleData list) {
    _postsList.add(list);
    notifyListeners();
  }

  mergePostsList(List<VehicleData> list, {bool notify = true}) {
    _postsList.addAll(list);
    if (notify) notifyListeners();
  }

  deletePost(VehicleData list) {
    _postsList.remove(list);

    notifyListeners();
  }

  addPost(VehicleData post, {bool notify = true}) {
    _postsList.add(post);
    if (notify) notifyListeners();
  }

  VehicleData getPostByIndex(int index) => _postsList[index];

  int get postsListLength => _postsList.length;
}

And this would be the Vehicle Data model:

class VehicleData {
  final String id,
      licencePlate,
      countryRegistered,
      type,
      color,
      vin,
      owner,
      garage,
      friendlyName,
      model,
      make,
      year,
      status,
      insuredBy,
      insurancePolicyNumber,
      policyExpirationDate,
      mobile,
      cellPhoneProvider,
      notes;
  final int capacity, co2perKm, luggageCapacity;

  VehicleData(
      {this.id,
      this.licencePlate,
      this.countryRegistered,
      this.type,
      this.color,
      this.vin,
      this.co2perKm,
      this.owner,
      this.garage,
      this.friendlyName,
      this.model,
      this.make,
      this.year,
      this.capacity,
      this.luggageCapacity,
      this.status,
      this.insuredBy,
      this.insurancePolicyNumber,
      this.policyExpirationDate,
      this.mobile,
      this.cellPhoneProvider,
      this.notes});

  factory VehicleData.fromJson(Map<String, dynamic> json) {
    return VehicleData(
      id: json['id'],
      licencePlate: json['licencePlate'],
      countryRegistered: json['countryRegistered'],
      type: json['type'],
      color: json['color'],
      vin: json['vin'],
      co2perKm: json['co2perKm'],
      owner: json['owner'],
      garage: json['garage'],
      friendlyName: json['friendlyName'],
      model: json['model'],
      make: json['make'],
      year: json['year'],
      capacity: json['capacity'],
      luggageCapacity: json['luggageCapacity'],
      status: json['status'],
      insuredBy: json['insuredBy'],
      insurancePolicyNumber: json['insurancePolicyNumber '],
      policyExpirationDate: json['policyExpirationDate '],
      mobile: json['mobile'],
      cellPhoneProvider: json['cellPhoneProvider '],
      notes: json['notes '],
    );
  }
}

And the API call:

import 'dart:convert';
import 'package:http/http.dart';;
import 'models/vehicle.dart';

Future<HTTPResponse<List<VehicleData>>> fetchVehicles() async {
  final response =
      await get('https://run.mocky.io/v3/d7a00528-176c-402f-850e-76567de3c68d');
  if (response.statusCode == 200) {
    var body = jsonDecode(response.body)['data'];
    List<VehicleData> posts = [];
    body.forEach((e) {
      VehicleData post = VehicleData.fromJson(e);
      posts.add(post);
    });
    return HTTPResponse<List<VehicleData>>(
      true,
      posts,
      message: 'Request Successful',
      statusCode: response.statusCode,
    );
  } else {
    return HTTPResponse<List<VehicleData>>(
      false,
      null,
      message:
          'Invalid data received from the server! Please try again in a moment.',
      statusCode: response.statusCode,
    );
  }
}
class HTTPResponse<T> {
  bool isSuccessful;
  T data;
  int statusCode;
  String message;
  HTTPResponse(this.isSuccessful, this.data, {this.message, this.statusCode});
}

Main:

import 'package:flutter/material.dart';
import 'components/vehicle_list.dart';
import 'screens/settings_screen.dart';
import 'package:provider/provider.dart';
import 'models/vehicle_data_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<HomePageProvider>(
            create: (_) => HomePageProvider()),
        ChangeNotifierProvider<AppLocale>(create: (_) => AppLocale()),
      ],
      child: Consumer<AppLocale>(builder: (context, locale, child) {
        return MaterialApp(
          home: VehicleList(),
        );
      }),
    );
  }
}

Like I said, I just need to update(edit) the existing data, which've passed from the one screen to another. Can someone explain to me what am I doing wrong when it comes to updating the data. I understand the updateList() method is exactly the same as the addNew oneis not working but I can't seem to find the solution.

Ok. So, I think I got it. The problem might be because you have two separate data sources. One is the API and the other is the HomePageProvider class. Now, what you're doing is that you're collecting data from the API and putting it in the HomePageProvider class. But every time the VehicleList() widget is called, the data is refreshed from the API. So, if you make any changes to your data in the HomePageProvider class, it will be overwritten by the data that comes from the API.

I would suggest you collect all the data in the HomePageProvider class itself and then call the API if and only if your _postsList variable has no data. Or determine some other measure to know if you should call the API or not. I would suggest using a Set instead of a List , if your data contains only unique values. It will help you avoid some unnecessary code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM