Smi*_*Kmc 3 flutter flutter-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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
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,
);
}
}
Run Code Online (Sandbox Code Playgroud)
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),
),
),
)
],
),
),
是的,对象是通过引用传递的。因此,您正在修改同一个对象。由于Flutter 中没有反射,因此您无法真正自动制作副本。
解决此问题的一种方法是实现您自己的copyWith方法。例如,在样式的情况下,这就是 Flutter 在内部所做的。
更新:重要的是要注意 List 和 Map 也是通过引用传递的。因此,你需要为使用List.from或传播运营商在自己的执行copyWith。
例子:
Entry(
images: images ?? List.from(this.images),
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
163 次 |
| 最近记录: |