I am using map for iterating the items on it like this,
...eventModifierProvider.selectedEvents.map((EventStore ) {
return Dismissible(
key: UniqueKey(),
onDismissed: (direction) {
eventModifierProvider.events.remove( what should be here?);
},
background: Container(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 20, 0),
child: Icon(Icons.close, size: 25, color: Colors.white),
),
decoration: BoxDecoration(color: Colors.redAccent)
),
child: Column(
children: [
TimeAndDuration(
time: "09:12",
amPm: "AM",
duration: "1 Hour 3 min",
),
ParticularDateEvent(
lectureSubject: EventStore.subject.toString(),
lectureStandard: EventStore.level.toString(),
lectureRoom: EventStore.room.toString(),
),
],
),
);
}),
In the selectedEvents map there is a key of DateTime and values are stored in EventStore list.
Now, I am also using the Dismissible widget for removing the item from the map. How can I do that here? Do help me
You need to get the index like this:
int index = eventModifierProvider.selectedEvents.indexOf(e);
Add the key
Key(index)
Remove it with index
RemoveAt(index)
I hope this is helpful
Related
This is the result that I want.
This is the current output that I have.
How can I achieve this result specifically the lines that are present in between the list tiles? I am using a ListView to show the rules. Below is the code for the ListTile that I am using.
Widget ruleTile(String title) {
return ListTile(
contentPadding: EdgeInsets.zero,
leading: Image.asset(
"assets/images/sun.png",
width: 40.w,
),
title: Text(
title,
style: MyTextStyle.littlesmaller,
),
);
}
You can use Stack and dotted_line package combo to create something like that:
Stack(children: [
DottedLine(
direction: Axis.vertical,
lineLength: linelength,
lineThickness: 1.0,
)
// You ListTile Code
],)
Thanks to #pmatatias comment, I figured it out. This is the updated code I used to get the desired output.
Widget ruleTile(String title, num index) {
Widget connector = const DashedLineConnector(
color: Color(0xFFFACC15),
gap: 3,
);
return TimelineTile(
node: TimelineNode(
indicator: Image.asset(
"assets/images/sun.png",
width: 40.w,
),
startConnector: index == 0 ? null : connector,
endConnector: index == rulesList.length - 1 ? null : connector,
),
nodeAlign: TimelineNodeAlign.start,
contents: ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
title,
style: MyTextStyle.littlesmaller,
),
),
);
}
I have a widget(s) to build that looks like this:
Being somewhat new to flutter, I'm drawing a blank on how to achieve the background being filled like that.
I do have my data like so:
enum ProgressKeys {
articles,
meals,
bloodPressure,
mood,
physicalActivity,
weight,
}
class StepProgress {
StepProgress({
required this.displayText,
required this.progress,
required this.count,
});
final String displayText;
final int progress;
int count;
}
final Map<ProgressKeys, StepProgress> programStepCounts = {
ProgressKeys.articles: {
displayText: "Articles Read",
progress: 2,
count: 4,
},
ProgressKeys.meals: {
displayText: "Meals Logged",
progress: 3,
count: 9,
},
// Etc...
}
All I can think of is start with a ListView.builder but I have no idea how to achieve something like this with the background.
You can use LinearProgressIndicator inside a Stack! Heres a code sample:
return Center(
child: Container(
clipBehavior: Clip.hardEdge,
margin: const EdgeInsets.symmetric(horizontal: 20.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
),
height: 80.0,
width: double.infinity,
child: Stack(
alignment: Alignment.centerLeft,
children: [
Positioned.fill(
child: LinearProgressIndicator(
//Here you pass the percentage
value: 0.7,
color: Colors.blue.withAlpha(100),
backgroundColor: Colors.blue.withAlpha(50),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Text('Hello world'),
)
],
),
),
);
You can pass to LinearProgressIndicator a value between 0 to 1 wich represents the percentage of completition. In the code sample i used 0.7 so its 70% full. It looks like this:
Of course you can change the colors and in the first container you can adjust the border.
I am currently working on the messaging section of my app. I am using a streambuilder and streams in conjunction with a GroupedListView so that the messages can be grouped according to date.
However, I am having some problems because the GroupedListView takes a list as its 'elements' parameter. And we know streams aren't necessarily lists. I have looked into converting streams to lists but I can't seem to find a solution.
Here's what the code looks like:
Expanded( //so that we can move the text field to the bottom
child: StreamBuilder(
stream: db.chatStream(widget.receiverUser.uid),
builder: (context, snapshot) {
return GroupedListView<Chat, DateTime>(
reverse: true, //so that the texts start from bottom to top
order: GroupedListOrder.DESC, //get the proper order of sent messages
padding: const EdgeInsets.all(8),
elements: db.chatStream(widget.receiverUser.uid), //THIS IS WHERE THE PROBLEM IS!!!
groupBy: (chat) => DateTime(
chat.dateTime.year,
chat.dateTime.month,
chat.dateTime.day
),
groupHeaderBuilder: (Chat chat) => SizedBox(
height: 40,
child: Center(
child: Card(
color: Colors.black45,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(
DateFormat.yMMMd().format(chat.dateTime),
style: const TextStyle(color: Colors.white, fontSize: 12),
),
),
),
),
),
itemBuilder: (context, Chat chat) {
bool isMe = chat.senderId == uid;
return Align(
alignment: isMe ? Alignment.centerRight
: Alignment.centerLeft,
child: Column(
children: [
Align(
alignment: isMe ? Alignment.centerRight
: Alignment.centerLeft,
child: Card(
color: isMe
? Colors.purpleAccent
: Colors.white,
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
chat.message,
style: TextStyle(
color: isMe
? Colors.white
: Colors.black
),
),
),
),
),
Align(
alignment: isMe
? Alignment.topRight
: Alignment.topLeft,
child: Padding(
padding: isMe ? const EdgeInsets.only(
right: 12)
: const EdgeInsets.only(left: 12),
child: MidText(text: DateFormat('kk:mm').format(
chat.dateTime)),
)
)
],
),
);
}
);
}
),
),
Is there a way to convert streams to lists so I can pass it to the "elements" parameter? Or do I need to take a different approach?
I also came across this SO post but it's without an answer. But this is essentially my same problem as well:
Example
I would thoroughly appreciate any help!
I'm not sure if what you asked for is something that exists. But what I would do is create a Stream<List<_YourType_>> and with the snapshot given, I would use the data as my list.
PS: If you initialize your StreamBuilder like StreamBuilder<List<_YourType_>>(... then your snapshot will be an AsyncSnapshot<List<_YourType_>> and its data value will already be a List<_YourType_> with no need to cast or anything!
PS2: If I were you, I would look for the time package as it has a .date getter for DateTime or even create your own like:
extension DateTimeExtension on DateTime {
DateTime get date => isUtc ? DateTime.utc(year, month, day) : DateTime(year, month, day);
}
Just so it's easier to get your dates without the time included.
You should probably declare your StreamBuilder as StreamBuilder<List>. You will run into an error saying object cannot access resource otherwise.
I created onboarding in my flutter app:
This is my data model:
class Data {
final String image;
final String title;
final String desc;
Data(this.image, this.title, this.desc); }
final List<Data> items = [
Data('assets/1.png', '1', '1'),
Data('assets/2.png', '2', '2'),
Data('assets/3.png', '3', '3')
];
This is my code:
Scaffold(
body: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
PageView.builder(
controller: _pageController,
itemBuilder: (BuildContext context, int index) {
var item = items[index];
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(item.image),
fit: BoxFit.cover,
),
),
//bloc1
child: Padding(
padding: const EdgeInsets.only(bottom: 80),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [Text(item.title), Text(item.desc)],
),
),
);
},
),
//bloc 2
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Padding(
padding: EdgeInsets.only(bottom: 10, top: 10),
child: Text('****dots indicators****'),
),
TextButton(
onPressed: () {},
child: Text('Next'),
)
],
))
],
),
);
This onBoading consists of three parts:
background image
text block - block1
"lower action block" - block2.
I need to have a small fixed distance between blocks 1 and 2 - 10pх.
but also I can't place block1 and block2 together. I need block1 to be "inside" PageView, block2 on the main page.
right now I'm using the bottom padding. but this is not a reliable solution - this size may change on different screens - "overlapping" will occur.
how can I fix this problem? Any advice - I'll be very grateful.
If you want to position your "page indicator dots" at a fixed relative spot, for example, 10% from the bottom of the screen, an easy solution is to use the Align widget.
However in your case, you must also change the Column in your to MainAxisSize.min, so that its height will be as small as possible, leaving you enough freedom to decide where to position the Column vertically. For example:
SafeArea(
child: Align(
alignment: Alignment(0, 0.95), // relative position
child: Column(
mainAxisSize: MainAxisSize.min, // make Column shorter
children: [
Text('****dots indicators****'),
TextButton(
onPressed: () {},
child: Text('Next'),
)
],
),
),
),
For the alignment property of the Align widget, the values are (x, y) ranging from -1 to +1, with 0 being the center. For example, you can use alignment: Alignment(0, -0.5) to make it center horizontally, and 50% towards the top on the vertical axis.
I am using Slidable inside a ListView so I can delete items. The problem is that if I remove an item from the list, the new item has its slidable open. That is obviously not the desired behavior. Here is a Screenvideo for a better understanding.
My list is an AnimatedList:
` child: AnimatedList(
padding: EdgeInsets.zero,
shrinkWrap: true,
key: listKey,
initialItemCount: widget.month.memories.length,
itemBuilder: (context, index, animation) {
return slideIt(
context,
widget.month.memories[index],
index,
animation,
);
},
),`
And for here is my Slidable:
` Widget slideIt(
BuildContext context,
Memory memory,
int index,
animation,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset(0, 0),
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut,
),
),
child: Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
movementDuration: Duration(milliseconds: 400),
child: Column(
children: [
MemoryTile(
memory: memory,
monthName: widget.month.name,
onTapped: () {
_removeMemoryAtIndex(index, memory);
print('tap on ${memory.description}');
},
),
SizedBox(
height: scaleWidth(20),
),
],
),
secondaryActions: [
SlideAction(
closeOnTap: false,
color: AppColors.secondary,
onTap: () => {
_removeMemoryAtIndex(index, memory),
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
CupertinoIcons.trash_fill,
color: AppColors.primary,
size: 30,
),
SizedBox(
height: scaleWidth(5),
),
Text(
'Löschen',
style: AppTextStyles.montserratH6SemiBold,
),
SizedBox(
height: scaleWidth(20),
),
],
),
),
],
),
);
}`
The AnimatedList makes it a bit more tricky so let me know if you need any more info! How can I fix this?
You should assign a key to your slidables. When Flutter manages its state it uses three trees to do it: a widget tree, element tree and render object tree. When a widget dismiss flutter tries to sync the rest widgets with the corresponding elements by simple type equality. So, that's why your app behaves this way. Flutter takes the first remaining widget plus the first element and compares whether they have the same type. If they have then Flutter uses the state of the element to show the corresponding render object. The last element of the element tree will be just dropped.
So, you need to add keys and Flutter will additionally use them to sync widgets and elements in a desirable way.
Each Flutter widget has a Key field. You should assign them when you creating a widget instance. You can use some utility classes to generate such a key - like ValueKey(), UniqueKey etc. Alternatively, you can use you own keys if you have it in your data.