【问题标题】:Flutter CRUD model with providerFlutter CRUD 模型与提供者
【发布时间】:2021-05-18 09:17:16
【问题描述】:

我正在使用 Provider 学习 Flutter 并尝试制作 CRUD 模型。我已经完成了创建部分、读取部分和删除部分,但是在模型的更新部分中遇到了困难。我进行了读取和显示数据的 API 调用,创建了一个创建新对象的方法和一个用于删除对象的方法,但剩下的就是修改/编辑对象。这是我到目前为止所做的代码:

调用 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),
                              ),
                            ],
                          );
                        },
                      ),
                    ),
                  ),
                ),
              ],
            );
          },
        ),
      ],
    );
  }
}

车辆详情屏幕

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();
                      }
                    },
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

这里是 HomePageProvider 模型:

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;
}

这就是车辆数据模型:

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 '],
    );
  }
}

API 调用:

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});
}

主要:

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(),
        );
      }),
    );
  }
}

就像我说的,我只需要更新(编辑)现有数据,这些数据已经从一个屏幕传递到另一个屏幕。有人可以向我解释在更新数据时我做错了什么。我知道updateList() 方法与 addNew 方法完全相同,但我似乎找不到解决方案。

【问题讨论】:

  • 你能分享你注册HomePageProvider的代码吗?这将帮助我理解问题。
  • @PreetShah 已添加到帖子中。

标签: flutter mobile crud flutter-dependencies flutter-provider


【解决方案1】:

好的。所以,我想我明白了。问题可能是因为您有两个单独的数据源。一个是 API,另一个是 HomePageProvider 类。现在,您正在做的是从 API 收集数据并将其放入 HomePageProvider 类中。但是每次调用 VehicleList() 小部件时,都会从 API 刷新数据。因此,如果您对 HomePageProvider 类中的数据进行任何更改,它将被来自 API 的数据覆盖。

我建议您收集 HomePageProvider 类本身中的所有数据,然后当且仅当您的 _postsList 变量没有数据时调用 API。或者确定一些其他措施来了解您是否应该调用 API。如果您的数据仅包含唯一值,我建议使用Set 而不是List。它将帮助您避免一些不必要的代码。

【讨论】:

  • 我会尝试改变这一点。您提到的想法是否有任何资源或完整示例?困扰我的是我已经完成了其他 3 个,但是在更新/编辑时,我不太明白出了什么问题..
  • 如果我的想法可行,请告诉我。看,问题是你需要一个分离视图、模型、存储库和控制器的架构。是否有任何功能可以让您直接将数据添加到 API 数据库?
  • 我虽然只用一个模型做了这个,当我调用API时,数据写入提供者(HomePageProvider),我做了一个添加和删除方法,但我有没有修改/编辑方法。有没有一种方法可以在提供程序中创建一个方法,或者我真的需要一种方法来将修改后的数据发送回 API 以及如果需要如何?
  • 看,问题是您直接从 API 检索数据并将其放入 Provider 中。您需要做的是检索 Provider 中的数据。因为每次加载 Widget VehicleList 时,都会调用函数 fetchVehicles()。因此,新数据每次都会在您的 Provider 中被覆盖。因此,请确保您在 HomePageProvider 中调用过一次。
  • 我明白你的意思,但我不明白我如何能够添加、删除它们而不是修改它们?我是不是没有在initState 中设置小部件的状态,所以它不会再次被调用,所以我可以通过 Provider 更改数据?很抱歉打扰您,但我做不到,我已经完成了所有其他工作,但没有修改/编辑。
猜你喜欢
  • 2020-09-01
  • 2021-01-19
  • 2022-01-12
  • 2021-09-23
  • 2020-10-16
  • 2010-09-14
  • 2010-09-29
  • 1970-01-01
  • 2020-04-29
相关资源
最近更新 更多