微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

更改一个 Provider 中的属性会将另一个 Provider 中的属性更改为 List Flutter 更新 - 我实际上发现它是一个 Flutter Issue

如何解决更改一个 Provider 中的属性会将另一个 Provider 中的属性更改为 List Flutter 更新 - 我实际上发现它是一个 Flutter Issue

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


我有两个提供者,一个是 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,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,)
        ..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,onpressed: () {
                                  closeDrawer();
                                },)
                            : IconButton(
                                icon: Icon(
                                  Icons.menu,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,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,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,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,)
                  ],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,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,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,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),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,controller: _descriptionController,decoration: Inputdecoration(
                    hintText:
                        MyLocalizations.of(context).entryScreenEnterDescription,style: TextStyle(fontSize: 18),_buildTagList(),SizedBox(
                height: 3,Container(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,children: <Widget>[
                    Text(
                      MyLocalizations.of(context).entryScreenImages,Consumer<EntryProvider>(
                builder: (context,child) => ImageList(
                  entryProvider.getEntry.images,_removeImage,_showPicker,_showImageDialog,SizedBox(
                height: 5,);
  }
}

解决方法

是的,对象是通过引用传递的。因此,您正在修改同一个对象。由于 there is no reflection in Flutter,您无法真正自动制作副本。

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

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

示例:

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。