Class 'String' has no instance method 'map' - flutter

I am using the flutter carousel code and trying to add images from firebase.
This is my code:
class FullscreenSliderDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF030164),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('quotes').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('Loading data... Please Wait');
final double height = MediaQuery.of(context).size.height;
return Container(
height: 100.0,
padding: const EdgeInsets.all(4.0),
child: new ListView(
scrollDirection: Axis.horizontal,
children: snapshot.data.documents
.map<Widget>((DocumentSnapshot document) {
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: (document.data()['img']).map(
(item) => Container(
child: Center(
child: Image.network(
item,
fit: BoxFit.cover,
height: height,
)),
),
).toList());
}).toList(),));
},
),
);
}
}
I am getting the error Class 'String' has no instance method 'map'. and I'm not sure how to resolve this. If anyone could help, I would appreciate it!

I think you're looking for something like this:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF030164),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('quotes').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('Loading data... Please Wait');
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: snapshot.data.documents
.map<Widget>((document) {
return Container(
child: Center(
child: Image.network(
document.data()['img'],
fit: BoxFit.cover,
height: height,
)
),
),
).toList()
)
)
},
),
);
}
With this you have only one CarouselSlider for all documents/images, instead of a separate one for each document.

Related

Flutter streambuilder returns wrong titles

I recieve ’title’ from firebase in a streambuilder but it gets all titles from all documents. I just want titles from the selected document.
StreamBuilder(
stream: Firestore.instance
.collection(widget.user.uid)
.orderBy('date', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
List titles =
activities.map((e) => e["title"]).toList();
return Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: $titles"),
),
);
}).toList(),
);
}
return const Text("no data");
},
)
What am I doing wrong? I just want to display title: lkjn and title:99okkj in a listview.
Just wrap widget creation inside of activitites.map(...) operation and make your widget list flat
Here is the updated version of the build method:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
return activities.map((e) => Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: ${e["title"]}"),
),
)
);
}).expand((element) => element).toList(),
));
}

Flutter Grid View Reorder & Drag Drop onto Another Item and Merge

I have been trying to add drag/drop support to my app, currently what I have come with is using this library:
reorderable_grid_view
I used this example code:
code link
The reason I used this library is that it's smooth enough of animations when dragging. But what I want to do is to drag one item to another so that I can merge the one to another object when I drop. (It's like in Android/iOS home screen where you can drag apps to folders or drag into another that it creates a folder)
I have searched all the site but couldn't come across with such thing, only drag/drop libraries are available. Can anyone help me on this?
Thanks in advance.
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
final ValueNotifier<List<ValueNotifier<List<Widget>>>> items = ValueNotifier([
ValueNotifier([Text("A")]),
ValueNotifier([Text("B")]),
ValueNotifier([Text("C")]),
ValueNotifier([Text("D")]),
]);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: ValueListenableBuilder(
valueListenable: items,
builder: (BuildContext context, List<ValueNotifier<List<Widget>>> folders, Widget? child) {
return GridView.builder(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemCount: folders.length,
itemBuilder: (context, index) {
ValueNotifier<List<Widget>> item = folders[index];
return LongPressDraggable(
delay: const Duration(milliseconds: 500),
feedback: SizedBox(width: MediaQuery.of(context).size.width / 4, height: MediaQuery.of(context).size.width / 4, child: FittedBox(child: Icon(Icons.folder))),
data: index,
childWhenDragging: const SizedBox(),
child: DragTarget(
onAccept: (data) {
List<Widget> alreadyHaved = item.value;
alreadyHaved.addAll(folders[data as int].value);
item.value = alreadyHaved;
items.value.removeAt(data);
items.notifyListeners();
},
builder: (context, candidateData, rejectedData) {
return ValueListenableBuilder(
valueListenable: item,
builder: (BuildContext context, List<Widget> boxValues, Widget? child) {
return Stack(children: [
const Positioned.fill(
child: FittedBox(
child: Icon(
Icons.folder,
color: Colors.amber,
))),
Positioned.fill(
child: LayoutBuilder(
builder: (p0, p1) => SizedBox(
height: p1.maxHeight * .7,
width: p1.maxWidth * .7,
child: Center(
child: Wrap(
children: boxValues,
),
))),
)
]);
},
);
},
),
);
});
},
),
),
),
],
),
);
}
}

Carousel slider with data retrieved from firebase realtime database

What I would like is to retrieve the data from a Firebase Realtime Database query instead of the List I created manually.
Here I am using a flutter package which is CarouselSlider and I am retrieving the data from a list written manually by myself while what I would like is to retrieve the content of my slider from a query.
Thanks for your help.
Here is my complete code:
import 'package:carousel_slider/carousel_slider.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flyzik/size_config.dart';
class SliderWidget extends StatefulWidget {
#override
State<SliderWidget> createState() => _SliderWidgetState();
}
final List<String> imgList = [
'https://firebasestorage.googleapis.com/v0/b/zfly2020-151d6.appspot.com/o/images_slide%2F3b1ab9f0-671a-11eb-bbaf-6ef0e5b93f8c.jpeg?alt=media&token=2b10908d-edb5-4527-9130-a5e076972e88',
'https://firebasestorage.googleapis.com/v0/b/zfly2020-151d6.appspot.com/o/images_slide%2FCopie%20de%20Red%20and%20black%20Black%20Friday%20sale%20Twitter%20post%20-%20Fait%20avec%20PosterMyWall.jpg?alt=media&token=2c39e317-859c-4560-963a-8374fe34fbcc',
'https://firebasestorage.googleapis.com/v0/b/zfly2020-151d6.appspot.com/o/images_slide%2F6169541506df6_61695418e5c29.jpg?alt=media&token=4c250834-d4f9-4946-93d6-c4bba58766c4',
'https://firebasestorage.googleapis.com/v0/b/zfly2020-151d6.appspot.com/o/images_slide%2F20211211_234252.jpg?alt=media&token=1cb1edcc-bf80-4fbd-a419-2c175e85997f',
'https://firebasestorage.googleapis.com/v0/b/zfly2020-151d6.appspot.com/o/images_slide%2Frap%20(1).jpg?alt=media&token=a2f3996f-b2d9-4cfd-81d2-1fe5c0becb6a'
];
class _SliderWidgetState extends State<SliderWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: CarouselSlider(
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
),
items: imageSliders,
),
);
}
final List<Widget> imageSliders = imgList
.map((item) => Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Center(
child: Image.network(
item,
fit: BoxFit.cover,
width: 1500.0,
height: getProportionateScreenHeight(300),
),
)),
),
))
.toList();
}
thank
This the result of all modification
First part
Second part
My database
You can fetch content with a method and use FutureBuilder to fetch it for you automatically.
Future<DataSnapshot> fetchList() async {
const path = 'SET YOUR PATH HERE';
return await FirebaseDatabase.instance.ref(path).get();
}
Wrap your Widget with FutureBuilder
#override
Widget build(BuildContext context) {
return FutureBuilder<DataSnapshot>(
future: fetchList(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasData && snapshot.data != null) {
final imgList = List<String>.from(snapshot.data?.value as List);
return Container(
child: CarouselSlider(
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
),
items: imageSliders(imgList),
),
);
}
);
return Text('Error');
}
final List<Widget> imageSliders(List<String> imgList) => imgList
.map((item) => Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Center(
child: Image.network(
item,
fit: BoxFit.cover,
width: 1500.0,
height: getProportionateScreenHeight(300),
),
)),
),
))
.toList();
}
Use imgList to map your data into Widget

Flutter page jumps to top after setState({})

I display many images in a Staggered Gridview in a Flutter application.
Everytime I call setState({}), for example after deleting an item, the page jumps to top. How could I remove this behavior?
This is my code:
final _scaffoldKey = new GlobalKey<ScaffoldState>();
.. outside the build function. And then...
return loadingScreen == true
? LoadingScreen()
: Scaffold(
key: _scaffoldKey,
body: CustomScrollView(
slivers: <Widget>[
_AppBar(
theme: theme,
index: index,
albumImagePath: albumImagePath,
albumID: albumID,
albumValue: albumValue,
addPictureToGallery: _addPictureToGallery,
),
SliverToBoxAdapter(
child: Column(
children: <Widget>[
InfoBar(
albumPicturesSum: albumPicturesSum,
getBilderString: _getBilderString,
theme: theme,
getVideoProgress: _getVideoProgress,
progress: progress,
),
albumID == 99999999
? // Demo Projekt
DemoImageGrid(
demoImageList: demoImageList,
getDemoImagesJson: _getDemoImagesJson,
)
: UserImageGrid(
picturesData: picturesData,
albumID: albumID,
showPictureActions: _showPictureActions)
],
),
)
],
),
);
}
The UserImageGrid looks like the following:
class UserImageGrid extends StatelessWidget {
final Pictures picturesData;
final int albumID;
final Function showPictureActions;
final _key = new UniqueKey();
UserImageGrid(
{#required this.picturesData,
#required this.albumID,
#required this.showPictureActions});
#override
Widget build(BuildContext context) {
return FutureBuilder(
key: _key,
future: picturesData.getPicturesFromAlbum(albumID),
builder: (BuildContext context, AsyncSnapshot snapshot) {
// Normale Projekte
if (snapshot.hasData && snapshot.data.length == 0) {
return Center(
child: Column(
children: <Widget>[
Lottie.asset('assets/lottie/drone.json',
width: 250,
options: LottieOptions(enableMergePaths: false)),
],
),
);
}
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0),
crossAxisCount: 6,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
GestureDetector(
onLongPress: () {
showPictureActions(snapshot.data[index]);
},
onTap: () async {
await showDialog(
context: context,
builder: (_) {
return Dialog(
child: Stack(
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0,
),
height: 500.0,
child: ClipRect(
child: PhotoView(
maxScale:
PhotoViewComputedScale.covered * 2.0,
minScale:
PhotoViewComputedScale.contained *
0.8,
initialScale:
PhotoViewComputedScale.covered,
imageProvider: FileImage(
File(snapshot.data[index].path))),
),
),
Positioned(
bottom: 20,
left: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
DateFormat(tr("date_format")).format(
snapshot.data[index].timestamp
.toDateTime()),
style: TextStyle(color: Colors.white),
),
),
)
],
));
});
},
child: Container(
child: Image.file(
File(snapshot.data[index].thumbPath),
fit: BoxFit.cover,
)),
),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 2),
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
);
}
});
}
}
What could be the issue?
I found a solution for this issue. The problem was not the setState({}). It was the return Widget of the FutureBuilder.
I changed
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
to:
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Container(
height: MediaQuery.of(context).size.height,
);
}
I don´t exactly know why, but with this change the page is not jumping to top anymore on setState({})

How to use Stack and listview together flutter

I have a ListView.builder inside of FutureBuilder where I get and display DB data to a screen.
Data are displayed one by one, but I'm wondering how to use the Stack widget in order to display them on each other.
https://github.com/geekruchika/FlutterCardSwipe that's how elements have to be positioned(scroll down)
SOLVED:
#override
Widget build(BuildContext context) {
var device = MediaQuery.of(context).size;
return FutureBuilder<List<Task>>(
future: DBHelper().getTasks(),
builder: (BuildContext context, AsyncSnapshot<List<Task>> snapshot) {
if (snapshot.hasData) {
var data = snapshot.data;
return snapshot.data.length > 0
? Stack(
children: data.map((task) {
return Positioned(
child: Dismissible(
key: UniqueKey(),
crossAxisEndOffset: -0.1,
onDismissed: (direction) {
DBHelper().delete(task.id);
},
child: Container(
height: device.height * 585 / 812,
child: AddTask(null, task.name,
'Meeting with directory', null, []),
),
),
);
}).toList(),
)
: Container(
height: device.height * 585 / 812,
child: NoTasksFound(),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
What you tried is correct except for you had to convert Iterable to a List.
Like:
Widget build(BuildContext context) {
var device = MediaQuery.of(context).size;
return FutureBuilder<List<Task>>(
future: DBHelper().getTasks(),
builder: (BuildContext context, AsyncSnapshot<List<Task>> snapshot) {
if (snapshot.hasData) {
var data = snapshot.data;
return Stack(
children: snapshot.data.map((task) { // map the data
return Positioned(
child: Dismissible(
key: UniqueKey(),
crossAxisEndOffset: -0.2,
background: Container(color: Colors.red),
onDismissed: (direction) {
// DBHelper().delete(task.id);
},
child: Container(
// margin: EdgeInsets.only(bottom: 100, top: 100),
height: device.height * 585 / 812,
child: AddTask(null, 'Description',
'Meeting with directory', null, []),
),
),
);
}).toList(), // convert the mapped iterable to list
);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
Hope that helps!