I have to show some text and images on a Card widget in some page.
I have a FlashCard
object containing Text (notetext
, date and images (a list of XFile
), whose instances are being stored in a Lesson
object.
The images are being shown by using the (XFile).path
.
This is my dialog for invoking the image_picker
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:spaced_repetition/data/lesson_data.dart';
import 'dart:io';
import 'package:spaced_repetition/pages/upcoming_reminders.dart';
Future<void> addFlashcardDialog(LessonMethods LessonProvider, Lesson lesson, BuildContext context, TextEditingController note, setState, selectedImages, ImagePicker imagepicker) async {
Future<void> pickMultipleImages(selectedImages, ImagePicker imagepicker) async {
if(Platform.isAndroid || Platform.isIOS){
final List<XFile?> images = await imagepicker.pickMultiImage();
if (images.isNotEmpty) {
setState((){
selectedImages.addAll(images);
});
}
} else {
final XFile? image = await imagepicker.pickImage(source: ImageSource.gallery);
setState((){
selectedImages.add(image);
});
}
print(selectedImages);
print(selectedImages.map((image) => image?.path));
}
void removeImage(int index, selectedImages) {
setState((){
selectedImages.removeAt(index);
});
}
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Add flashcard"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200),
child: TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
controller: note,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Add note here',
),
),
),
SizedBox(height: 20),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200, minHeight: 100),
child: Row(children: [
GestureDetector(
onTap: (){pickMultipleImages(selectedImages, imagepicker);},
child: Container(
width: 40,
height: 90,
child: Card(
child: Text('+nAdd images', textAlign: TextAlign.center,),
),
)
),
SizedBox(width: 5),
Expanded(
child: SizedBox(
height: 90,
width: 100,
child: ListView.builder(shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: selectedImages.length, itemBuilder: (context, index){
print("okeh:");
print(selectedImages);
return Container(
width: 40,
height: 90,
child: Stack(
children: [
Card(
child: Container(
width: 40,
height: 90,
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(File(selectedImages[index]?.path ?? "")),
fit: BoxFit.cover
),
borderRadius: BorderRadius.circular(8.0),
),
),
),
Positioned(
right: 8,
top: 8,
child: TextButton(
onPressed: () => {removeImage(index, selectedImages)}, // Remove the last image
style: TextButton.styleFrom(
backgroundColor: Colors.redAccent.withOpacity(0.7), // Background color for button
minimumSize: Size(32, 32), // Minimum button size
shape: CircleBorder(), // Circular button shape
),
child: Text(
'X',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
]
)
);
}),
)
),
],)
),
],
),
),
actions: <Widget>[
ElevatedButton(
child: const Text('Add flashcard'),
onPressed: (){
LessonProvider.addFlashCard(lesson, note.text, selectedImages);
Navigator.of(context).pop();
note.clear();
selectedImages.clear();
}
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
note.clear();
selectedImages.clear();
},
child: const Text("Cancel"),
),
],
);
});
}
And this is where the dialog is being run from (a different file)
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:spaced_repetition/data/lesson_data.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:spaced_repetition/dialogs/flashcard_dialogs.dart';
import 'package:image_picker/image_picker.dart';
class upcomingReminders extends StatefulWidget {
const upcomingReminders({super.key});
@override
State<upcomingReminders> createState() => _upcomingRemindersState();
}
class _upcomingRemindersState extends State<upcomingReminders> {
late TextEditingController flashcardnotecontroller;
@override
void initState() {
super.initState();
flashcardnotecontroller = TextEditingController();
}
@override
Widget build(BuildContext context) {
final LessonProvider = Provider.of<LessonMethods>(context);
List lessons = LessonProvider.lessons;
final ImagePicker imagepicker = ImagePicker();
List<XFile?> selectedImages = [];
//a lot of unrelated code later, in a ListView.builder
if(selectedCardOption == CardOptions.addflashcard){
addFlashcardDialog(LessonProvider, lesson, context, flashcardnotecontroller, setState, selectedImages, imagepicker);
}
And here is my Provider
file which contains the class descriptions and the addFlashcardDialog method
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
class Lesson {
// a lot of unrelated object members
List<FlashCard> flashCards = [];
Lesson({
//initialising other members
});
}
enum CardOptions { done, deletethis, deleteall, addflashcard }
class FlashCard {
DateTime notedate;
String note;
List<XFile?> images;
FlashCard({
required this.notedate,
required this.note,
required this.images,
});
}
//a lot of unrelated methods later
void addFlashCard(Lesson lesson, String note, List<XFile?> images){
lesson.flashCards.add(FlashCard(notedate: DateTime.now(),note: note, images: images));
notifyListeners();
}
And the page where the flashcards appear
import 'package:flutter/material.dart';
import 'package:spaced_repetition/data/lesson_data.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import 'dart:io';
class FlashCardsPage extends StatefulWidget {
const FlashCardsPage({super.key, required this.lesson});
final Lesson lesson;
@override
State<FlashCardsPage> createState() => _FlashCardsPageState();
}
class _FlashCardsPageState extends State<FlashCardsPage> {
late Lesson lesson;
@override
void initState(){
lesson = widget.lesson;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(lesson.lessonName, overflow: TextOverflow.ellipsis,),
),
body: ListView.builder(
itemCount: lesson.flashCards.length,
itemBuilder: (context, index){
return Card(
elevation: 4.0,
margin: EdgeInsets.all(16.0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Date on Top
Text(
DateFormat('dd-MM-yyyy').format(lesson.flashCards[index].notedate),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
// Horizontally Scrollable List of Images
Container(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: lesson.flashCards[index].images.length,
itemBuilder: (context, imageindex) {
print(lesson.flashCards[index].images[imageindex]?.path);
print('yap');
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.file(
File(lesson.flashCards[index].images[imageindex]?.path ?? ""),
fit: BoxFit.cover,
width: 100,
height: 100,
),
),
);
},
),
),
SizedBox(height: 16),
// Note Text
Text(
lesson.flashCards[index].note,
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
),
),
],
),
),
);
}
)
);
}
}
The problem is that while the text and dates are being shown, the images are not, even though they are being collected by the selectedImage list. The image instances and their path is being shown in print(selectedImages); print(selectedImages.map((image) => image?.path));
, but apparently the lists containing the images arent even being rendered, neither in the dialog nor in the FlashCards page, as the print(lesson.flashCards[index].images[imageindex]?.path); print('yap');
in the FlashCards page do not return anything.