更改一个 Provider 中的属性会将另一个 Provider 中的属性更改为 List Flutter

Posted

技术标签:

【中文标题】更改一个 Provider 中的属性会将另一个 Provider 中的属性更改为 List Flutter【英文标题】:Changing property in one Provider changes the property in another Provider as a List Flutter 【发布时间】:2021-04-26 23:00:49 【问题描述】:

更新 - 我实际上发现它是一个Flutter Issue。


我有两个 Provider,一个是 EntriesProvider,另一个是 EntryProvider。我在创建条目时使用我的 EntryProvider 并使用我的 EntriesProvider 来加载保存到数据库中的所有条目。我遇到了一个问题,我认为这可能是我对如何使用 Providers 的理解。一旦我将数据库数据加载到我的 EntriesProvider 中,我就会将该数据加载到 ListView 中。单击某个项目后,我将该列表中的条目传递到我的视图中以进行填充和编辑。

我的问题是,当我编辑条目而不保存它时,我可以看到 ListView 中发生的更改不是我想要的。我尝试清除 EntryProvider,因为我认为属于它的数据与 EntriesProvider 是分开的。但是现在我尝试了多种方法后不知道。为什么我只要求 EntryProvider 更新其侦听器时要更新列表?

class EntryProvider extends ChangeNotifier 
  Entry _entry;
  BuildContext context;

  EntryProvider();

  Entry get getEntry 
    return _entry;
  

  void setEntryContext(Entry entryToBeSet, BuildContext context) 
    this._entry = entryToBeSet;
    this.context = context;
    notifyListeners();
  

  void clearEntryContext() 
    this._entry = null;
    this.context = null;
    notifyListeners();
  

  void addImageToEntry(String imagePath) 
    getEntry.images.add(imagePath);
    notifyListeners();
  

  void removeImageAt(int index) 
    getEntry.images.removeAt(index);
    notifyListeners();
  

  void addTagToEntry(String tagText) 
    getEntry.tags.add(tagText);
    notifyListeners();
  

  void removeTagAt(int index) 
    getEntry.tags.removeAt(index);
    notifyListeners();
  

  Future<void> saveEntry() async 
    if (getEntry.id != null) 
      await Provider.of<EntriesProvider>(context, listen: false)
          .updateEntry(getEntry);
     else 
      await Provider.of<EntriesProvider>(context, listen: false)
          .addEntry(getEntry);
    
  

class EntriesProvider extends ChangeNotifier 
  List<Entry> _entries = [];

  EntriesProvider(this._entries);

  UnmodifiableListView<Entry> get entries => UnmodifiableListView(_entries);

  int get length => _entries.length;

  List<Entry> get getEntriesSortedByDateReversed 
    List<Entry> entriesCopy = entries;
    entriesCopy.sort((a, b) => a.entryDate.compareTo(b.entryDate));

    return entriesCopy.reversed.toList();
  

  List<Entry> getEntries(DateTime dateTime) 
    List<Entry> entriesToBeSorted = entries
        .where(
          (entry) => DateFormat.yMMMd().format(entry.entryDate).contains(
                DateFormat.yMMMd().format(dateTime),
              ),
        )
        .toList();

    entriesToBeSorted.sort((a, b) 
      return a.entryDate.compareTo(b.entryDate);
    );

    return entriesToBeSorted;
  

class JournalListView extends StatefulWidget 
  bool isDrawerOpen;
  final TransformData transformData;

  JournalListView(this.isDrawerOpen, this.transformData);

  @override
  _JournalListScreenState createState() => _JournalListScreenState();


class _JournalListScreenState extends State<JournalListView> 
  List<Entry> entries = [];
  List<Entry> filteredEntries = [];
  DateTime dateTimeSet;

  AppDataModel appDataModel;

  @override
  void initState() 
    super.initState();
    dateTimeSet = DateTime.now();
  

  Widget _buildEntryList(BuildContext context) 
    return Consumer<EntriesProvider>(builder: (context, entryModel, child) 
      print(entryModel.entries);
      List<Entry> entries = entryModel.getEntries(dateTimeSet);
      return Container(
        constraints: BoxConstraints(
          maxHeight: 650,
          maxWidth: double.infinity,
        ),
        child: Container(
          child: entries.length > 0
              ? ListView.builder(
                  itemCount: entries.length,
                  padding: EdgeInsets.all(2.0),
                  itemBuilder: (context, index) 
                    return InkWell(
                      onTap: () 
                        if (widget.isDrawerOpen) 
                          closeDrawer();
                         else 
                          Navigator.of(context).push(
                            PageRouteBuilder(
                                transitionDuration: Duration(milliseconds: 650),
                                pageBuilder:
                                    (context, animation, secondaryAnimation) 
                                  final Entry copiedEntry = entries[index]
                                      .copyWith(
                                          id: entries[index].id,
                                          title: entries[index].title,
                                          description:
                                              entries[index].description,
                                          entryDate: entries[index].entryDate,
                                          feelingOnEntry:
                                              entries[index].feelingOnEntry,
                                          images: entries[index].images,
                                          location: entries[index].location,
                                          tags: entries[index].tags,
                                          time: entries[index].time,
                                          weather: entries[index].weather);
                                  Provider.of<EntryProvider>(context, listen: false)
        .setEntryContext(entry, context);
                                  return JournalEntryView(copiedEntry);
                                ),
                          );
                        
                      ,
                      child: Hero(
                        tag: '$entries[index].entryDate$entries[index].id',
                        child: _buildEntryLayout(context, entries[index]),
                      ),
                    );
                  ,
                )
              : JournalEmpty(
                  'lib/assets/emojis/empty-folder.png',
                  MyLocalizations.of(context).journalListEmpty,
                ),
        ),
      );
    );
  

  Widget _buildEntryLayout(BuildContext context, Entry entry) 
    int entryLayout = appDataModel.entryLayout;
    Widget entryLayoutWidget;

    switch (entryLayout) 
      case 1:
        entryLayoutWidget = EntryCard1(entry);
        break;
      case 2:
        entryLayoutWidget = EntryCard2(entry);
        break;
      default:
        entryLayoutWidget = EntryCard1(entry);
        break;
    

    return entryLayoutWidget;
  

  Widget _buildCalenderStrip(BuildContext context) 
    return Container(
      height: 64,
      margin: const EdgeInsets.all(2.0),
      child: Consumer<EntriesProvider>(
        builder: (context, entryModel, child) 
          return Calendarro(
              startDate: DateUtils.getFirstDayOfMonth(DateTime(2020, 09)),
              endDate: DateUtils.getLastDayOfCurrentMonth(),
              selectedSingleDate: DateTime.now(),
              displayMode: DisplayMode.WEEKS,
              dayTileBuilder: CustomDayBuilder(entryModel.entries),
              onTap: (datetime) 
                if (widget.isDrawerOpen) 
                  closeDrawer();
                
                setState(() 
                  dateTimeSet = datetime;
                );
              );
        ,
      ),
    );
  

  Widget _buildSearchEntryWidget(BuildContext context) 
    return Consumer<EntriesProvider>(builder: (context, entries, child) 
      return IconButton(
        onPressed: () => showSearch(
          context: context,
          delegate: SearchPage<Entry>(
            items: entries.entries,
            searchLabel: MyLocalizations.of(context).journalListSearchEntries,
            suggestion: Center(
              child: Text(MyLocalizations.of(context).journalListFilterEntries),
            ),
            failure: JournalEmpty(
              'lib/assets/emojis/no_items.png',
              MyLocalizations.of(context).journalListNoEntriesFound,
            ),
            filter: (entry) 
              List<String> filterOn = List<String>();
              filterOn.add(entry.title);
              if (entry.tags != null) 
                entry.tags.forEach((tag) => filterOn.add(tag));
              
              return filterOn;
            ,
            builder: (entry) => InkWell(
              onTap: () 
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) => JournalEntryView(entry),
                  ),
                );
              ,
              child: EntryCard1(
                entry,
              ),
            ),
          ),
        ),
        icon: Icon(
          Icons.search,
          size: 30,
          color: Theme.of(context).primaryColor,
        ),
      );
    );
  

  void closeDrawer() 
    setState(() 
      widget.transformData.xOffset = 0;
      widget.transformData.yOffset = 0;
      widget.transformData.scaleFactor = 1;
      widget.isDrawerOpen = false;
    );
  

  bool isDateChoosenValid() 
    return dateTimeSet.compareTo(DateTime.now()) < 1;
  

  @override
  Widget build(BuildContext context) 
    appDataModel = Provider.of<AppDataProvider>(context).appDataModel;

    return AnimatedContainer(
      transform: Matrix4.translationValues(
        widget.transformData.xOffset,
        widget.transformData.yOffset,
        0,
      )
        ..scale(widget.transformData.scaleFactor)
        ..rotateY(widget.isDrawerOpen ? -0.5 : 0),
      duration: Duration(milliseconds: 250),
      decoration: BoxDecoration(
        color: Colors.grey[200],
        borderRadius: BorderRadius.circular(
          widget.isDrawerOpen ? 25 : 0.0,
        ),
      ),
      child: GestureDetector(
        onTap: () 
          if (widget.isDrawerOpen) 
            closeDrawer();
          
        ,
        child: ClipRRect(
          borderRadius: BorderRadius.circular(25),
          child: Scaffold(
              body: Column(
                children: [
                  SizedBox(
                    height: 30,
                  ),
                  Container(
                    margin: EdgeInsets.symmetric(horizontal: 20),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        widget.isDrawerOpen
                            ? IconButton(
                                icon: Icon(
                                  Icons.arrow_back,
                                  size: 30,
                                  color: Theme.of(context).primaryColor,
                                ),
                                onPressed: () 
                                  closeDrawer();
                                ,
                              )
                            : IconButton(
                                icon: Icon(
                                  Icons.menu,
                                  size: 30,
                                  color: Theme.of(context).primaryColor,
                                ),
                                onPressed: () 
                                  setState(() 
                                    widget.transformData.xOffset = 260;
                                    widget.transformData.yOffset = 150;
                                    widget.transformData.scaleFactor = 0.7;
                                    widget.isDrawerOpen = true;
                                  );
                                ),
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.end,
                          children: [
                            Text(
                              Constants.APP_NAME,
                              style: TextStyle(
                                fontSize: 28,
                                color: Theme.of(context).primaryColor,
                                fontWeight: FontWeight.w500,
                              ),
                            ),
                          ],
                        ),
                        _buildSearchEntryWidget(context)
                      ],
                    ),
                  ),
                  SizedBox(
                    height: 5,
                  ),
                  _buildCalenderStrip(context),
                  _buildEntryList(context),
                ],
              ),
              floatingActionButtonLocation:
                  FloatingActionButtonLocation.endFloat,
              floatingActionButton: isDateChoosenValid()
                  ? OpenContainer(
                      transitionDuration: Duration(milliseconds: 600),
                      closedBuilder: (BuildContext c, VoidCallback action) =>
                          FloatingActionButton(
                        onPressed: null,
                        child: Icon(
                          Icons.edit,
                          size: 30,
                        ),
                        tooltip:
                            MyLocalizations.of(context).journalListAddEntry,
                        backgroundColor: isDateChoosenValid()
                            ? Theme.of(context).primaryColor
                            : Colors.grey[500],
                        elevation: 8.0,
                      ),
                      closedShape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(100)),
                      openBuilder: (BuildContext c, VoidCallback action) 
                        final entry = Entry(
                          entryDate: dateTimeSet,
                          images: List<Object>(),
                          tags: List<String>(),
                        );
                        return JournalEntryView(entry);
                      ,
                      tappable: isDateChoosenValid(),
                    )
                  : SizedBox()),
        ),
      ),
    );
  


class CustomDayBuilder extends DayTileBuilder 
  final List<Entry> entries;
  CustomDayBuilder(this.entries);

  @override
  Widget build(BuildContext context, DateTime date, onTap) 
    Entry entry = entries.firstWhere(
      (entryInEntries) => DateFormat.yMMMd()
          .format(entryInEntries.entryDate)
          .contains(DateFormat.yMMMd().format(date)),
      orElse: () => Entry(),
    );
    return CustomDateTile(
      date: date,
      entry: entry,
      calendarroState: Calendarro.of(context),
      onTap: onTap,
    );
  

class JournalEntryView extends StatefulWidget 
  final Entry entry;

  JournalEntryView(this.entry);

  @override
  _JournalEntryScreenState createState() => _JournalEntryScreenState();


class _JournalEntryScreenState extends State<JournalEntryView> 
  GlobalKey _scaffoldKey = GlobalKey<ScaffoldState>();

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

  @override
  Widget build(BuildContext context) 
    Entry entry = widget.entry;
    Provider.of<EntryProvider>(context, listen: false)
        .setEntryContext(entry, context);
    return Hero(
      tag: '$entry.entryDate$entry.id',
      child: Form(
        child: Builder(
          builder: (ctx) 
            return WillPopScope(
              child: Scaffold(
                key: _scaffoldKey,
                resizeToAvoidBottomPadding: true,
                backgroundColor: Theme.of(context).primaryColor,
                appBar: AppBar(
                  actionsIconTheme: IconThemeData(color: Colors.white),
                  iconTheme: IconThemeData(color: Colors.white),
                  actions: <Widget>[
                    IconButton(
                      onPressed: () async 
                        Form.of(ctx).save();
                        if (!Form.of(ctx).validate()) 
                          return;
                        

                        if (Provider.of<EmojiListProvider>(context,
                                    listen: false)
                                .getChosenFeeling ==
                            null) 
                          _showFormError(
                            MyLocalizations.of(context).journalEntryNeedMood,
                          );
                          return;
                         else 
                          entry.feelingOnEntry = entry.getFeeling(
                              Provider.of<EmojiListProvider>(context,
                                      listen: false)
                                  .getChosenFeeling
                                  .url);
                        

                        if (entry.time == null) 
                          entry.time = DateFormat.Hm().format(DateTime.now());
                        

                        entry.weather = 'Sunny';
                        Provider.of<EntryProvider>(context, listen: false)
                            .saveEntry();
                        Navigator.of(context).pop();
                      ,
                      padding: EdgeInsets.only(right: 16),
                      icon: Icon(
                        Icons.save,
                        color: Colors.white,
                        size: 25,
                      ),
                    )
                  ],
                  backgroundColor: Theme.of(context).primaryColor,
                  elevation: 0.0,
                  shadowColor: Theme.of(context).primaryColor,
                  bottomOpacity: 0.0,
                ),
                body: Stack(
                  children: <Widget>[
                    Column(
                      children: <Widget>[
                        Expanded(
                          child: Container(
                            color: Theme.of(context).primaryColor,
                            alignment: Alignment.topCenter,
                            child: Container(
                              child: Column(
                                children: [
                                  Container(
                                    margin:
                                        EdgeInsets.only(left: 20, bottom: 5),
                                    child: Text(
                                      MyLocalizations.of(context)
                                          .journalEntryFeeling,
                                      style: TextStyle(
                                        color: Colors.white,
                                        fontSize: 22,
                                        fontWeight: FontWeight.bold,
                                      ),
                                    ),
                                    alignment: Alignment.topLeft,
                                  ),
                                  FeelingsList(entry.feelingOnEntry),
                                ],
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                    Container(
                      alignment: Alignment.bottomCenter,
                      padding: EdgeInsets.only(top: 115),
                      child: Container(
                        width: double.infinity,
                        child: ClipRRect(
                          borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(80),
                          ),
                          child: EntryScreenData(entry),
                        ),
                      ),
                    )
                  ],
                ),
              ),
              onWillPop: () 
                Provider.of<EntryProvider>(context, listen: false)
                    .clearEntryContext();
                Provider.of<EmojiListProvider>(context, listen: false)
                    .setEmojiList();
                Navigator.pop(context);
                return;
              ,
            );
          ,
        ),
      ),
    );
  

  void _showFormError(String errorText) 
    final snackBar = SnackBar(
      backgroundColor: Colors.red[400],
      content: Text(errorText),
    );
  


class EntryScreenData extends StatefulWidget 
  final Entry entry;
  List<Object> images;

  EntryScreenData(this.entry);

  @override
  _EntryScreenDataState createState() => _EntryScreenDataState();


class _EntryScreenDataState extends State<EntryScreenData> 
  final SettingsDataModel settingsDataModel =
      SettingsDataModel.fromJson(jsonDecode(sharedPrefs.settingsData));
  final _titleController = TextEditingController();
  final _descriptionController = TextEditingController();
  final Geolocator geolocator = Geolocator()..forceandroidLocationManager;

  DateTime datePicked;

  @override
  void dispose() 
    _titleController.dispose();
    _descriptionController.dispose();
    super.dispose();
  

  @override
  void initState() 
    if (widget.entry.weather == null) 
      widget.entry.weather = 'Sunny';
    

    _titleController.value = TextEditingValue(
      text: widget.entry.title != null ? widget.entry.title : '',
      selection: TextSelection.collapsed(
        offset: widget.entry.title != null ? widget.entry.title.length : 0,
      ),
    );

    _descriptionController.value = TextEditingValue(
      text: widget.entry.description != null ? widget.entry.description : '',
      selection: TextSelection.collapsed(
        offset: widget.entry.description != null
            ? widget.entry.description.length
            : 0,
      ),
    );

    widget.entry.entryDate != null
        ? datePicked = widget.entry.entryDate
        : datePicked = DateTime.now();

    widget.entry.tags != null
        ? widget.entry.tags = widget.entry.tags
        : widget.entry.tags = List<dynamic>();

    super.initState();
  

  Future<String> getImage(int type) async 
    PickedFile pickedImage = await ImagePicker().getImage(
        source: type == 1 ? ImageSource.camera : ImageSource.gallery,
        imageQuality: 50);
    return pickedImage.path;
  

  _imgFromCamera() async 
    final imagePath = await getImage(1);
    Provider.of<EntryProvider>(context, listen: false)
        .addImageToEntry(imagePath);
  

  // HERE FOR INSTANCE IS WHERE I@M MAKING A CHANGE TO THE ENTRY THAT SHOWS ON THE LIST
  _imgFromGallery() async 
    final imagePath = await getImage(2);
    Provider.of<EntryProvider>(context, listen: false)
        .addImageToEntry(imagePath);
  

  
  Widget _buildTagList() 
    return Container(
      height: 71,
      margin: EdgeInsets.only(top: 5, bottom: 5),
      child: Column(
        children: <Widget>[
          Container(
            alignment: Alignment.topLeft,
            child: Text(MyLocalizations.of(context).entryScreenTags,
                style: TextStyle(fontSize: 18)),
          ),
          Consumer<EntryProvider>(
            builder: (context, entryProvider, child) => CreateHashtags(
              entryProvider.getEntry.tags,
              _addTag,
              _removeTag,
            ),
          ),
        ],
      ),
    );
  

  void _addTag(String tagText) 
    Provider.of<EntryProvider>(context, listen: false).addTagToEntry(tagText);
  

  void _removeTag(int index) 
    Provider.of<EntryProvider>(context, listen: false).removeTagAt(index);
  

  void _removeImage(int index) 
    Provider.of<EntryProvider>(context, listen: false).removeImageAt(index);
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      resizeToAvoidBottomPadding: true,
      body: Container(
        alignment: Alignment.topCenter,
        color: Colors.white,
        padding: EdgeInsets.only(
          left: 20,
          right: 20,
        ),
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              EntryMetaTags(widget.entry, _getAddressFromLatLng),
              SizedBox(
                height: 10,
              ),
              Container(
                alignment: Alignment.topLeft,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    InkWell(
                      onTap: _presentDatePicker,
                      child: Text(
                        DateFormat.yMMMd().format(
                          widget.entry.entryDate != null
                              ? widget.entry.entryDate
                              : DateTime.now(),
                        ),
                        style: TextStyle(fontSize: 24),
                      ),
                    ),
                    if (widget.entry.id != null)
                      IconButton(
                        onPressed: () 
                          _showDeleteDialog(context);
                        ,
                        icon: Icon(
                          Icons.delete,
                          color: Theme.of(context).primaryColor,
                        ),
                      ),
                  ],
                ),
              ),
              Container(
                alignment: Alignment.topLeft,
                child: TextFormField(
                  onSaved: (String title) 
                    Provider.of<EntryProvider>(context, listen: false)
                        .getEntry
                        .title = title;
                  ,
                  textCapitalization: TextCapitalization.sentences,
                  controller: _titleController,
                  decoration: InputDecoration(
                    hintText: MyLocalizations.of(context).entryScreenEnterTitle,
                    contentPadding: EdgeInsets.all(0),
                    border: InputBorder.none,
                    focusedBorder: OutlineInputBorder(
                      borderSide: BorderSide(width: 0, color: Colors.white),
                    ),
                  ),
                  style: TextStyle(fontSize: 20),
                ),
              ),
              Container(
                height: 190,
                margin: EdgeInsets.only(top: 5),
                alignment: Alignment.topLeft,
                child: TextFormField(
                  onSaved: (String description) 
                    Provider.of<EntryProvider>(context, listen: false)
                        .getEntry
                        .description = description;
                  ,
                  validator: (description) 
                    if (description.isEmpty) 
                      return MyLocalizations.of(context)
                          .entryScreenEnterDescriptionWarn;
                    

                    return null;
                  ,
                  maxLines: 8,
                  keyboardType: TextInputType.text,
                  textCapitalization: TextCapitalization.sentences,
                  controller: _descriptionController,
                  decoration: InputDecoration(
                    hintText:
                        MyLocalizations.of(context).entryScreenEnterDescription,
                    contentPadding: EdgeInsets.all(0),
                    border: InputBorder.none,
                    focusedBorder: OutlineInputBorder(
                      borderSide: BorderSide(width: 0, color: Colors.white),
                    ),
                  ),
                  style: TextStyle(fontSize: 18),
                ),
              ),
              _buildTagList(),
              SizedBox(
                height: 3,
              ),
              Container(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text(
                      MyLocalizations.of(context).entryScreenImages,
                      style: TextStyle(fontSize: 18),
                    ),
                  ],
                ),
              ),
              Consumer<EntryProvider>(
                builder: (context, entryProvider, child) => ImageList(
                  entryProvider.getEntry.images,
                  _removeImage,
                  _showPicker,
                  _showImageDialog,
                ),
              ),
              SizedBox(
                height: 5,
              ),
            ],
          ),
        ),
      ),
    );
  

【问题讨论】:

尝试使用 Bloc 模式,它使用流来更新 ui。 Provider 主要用于跨多个屏幕提供数据。由于它具有更新两个属性的错误,您可能想尝试 bloc。 【参考方案1】:

是的,对象是通过引用传递的。因此,您正在修改同一个对象。因为there is no reflection in Flutter,你不能真正自动复制。

解决此问题的一种方法是实现您自己的copyWith 方法。例如,这就是 Flutter 在内部对样式所做的事情。

更新:重要的是要注意 List 和 Map 也是通过引用传递的。因此,您需要在自己的copyWith 实现中使用List.from 或spread operator。

例子:

Entry(
 images: images ?? List.from(this.images),
);

【讨论】:

我不知道为什么“是的,对象是通过引用传递的。因此,您正在修改同一个对象。”对我来说太难了。我猜其他语言和框架在某种程度上会宠爱开发人员。好的,我要试试copyWith。迄今为止,它对我来说一直是一个巨大的障碍,我不明白为什么它不起作用。 表示对象本身并没有被复制,只有指向它的内存地址。但是由于地址的副本指向内存中的同一个地方,所以同一个对象正在被修改。 哦,我明白这意味着什么,我只是不明白为什么 Dart/Flutter 工程师会这样做。不过还是谢谢你的解释。我按照你的解释尝试了实现,我注意到一开始它看起来很有效,但是当我回到项目卡时,我看到变化仍然存在,然后当我按下时,就像原来的一样问题? 我已经在我的代码中实现了 copyWith 更改,我已经添加了我的 Entry 类并显示了在 ListView 上所做的编辑。但我仍然遇到问题。实施的方式是正确的方式吗? 感谢您花时间帮助我理解这一点!我要离开,再好好想想!

以上是关于更改一个 Provider 中的属性会将另一个 Provider 中的属性更改为 List Flutter的主要内容,如果未能解决你的问题,请参考以下文章

使用将另一列数据作为输入的函数更改列数据

使用 AJAX/PHP 将另一个站点表中的内容插入我的表单?

Extjs 将另一个商店作为另一个属性绑定到网格

按值传递结构,将另一个结构作为其成员之一,更改此成员的成员的值

检查proc附加是否成功

mybatis映射文件,当从XXXDao.java中传入的参数是一个对象Provider的时候,那在XXXDao.xml中的Provider的属性id的时候需要怎么写