I need to click on the list, and open a new detail page. I've looked everywhere for examples of how to do this and I found many. However my data is structured different and even though my code has no errors it will not populate the details page. Here is what i'm working with:
//page 1
child: ListView.builder(
itemCount:
_documents == null ? 0 : _documents.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/images/roomfield1.png"),
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter),
color:
Theme.of(context).canvasColor,
),
child: ListTile(
title: Text(_documents[index]
['title']),
subtitle: Text(_documents[index]
["description"]),
trailing: Text(_documents[index]
['citystate']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailPage(_documents[index])));
})));
},
Here is the details page:
// details page
import 'package:flutter/material.dart';
class DetailPage extends StatefulWidget {
DetailPage(_documents, {Key key,}) : super(key: key);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
var _documents;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: ListView.builder(
itemCount: _documents == null ? 0 : _documents.length,
itemBuilder: (BuildContext context,[index]) {
return ListTile(
title: Text(_documents[index]['title']),
);
}));}}
My documents use the documentId. i tried passing the id and then using it in a stream builder and that didn't work either. I think my code is close, but I don't know what is wrong. Also I cant change the structure of the data, because it is used all over the place in this format. I'm guessing its something simple that i missed, maybe the constructor on the details page but i'm not sure...Any insight is appreciated.
You created a variable called _documents at the top of the class. However, to retrieve the value of the actual _documents variable in the DetailPage class, you need to use widget.documents. I have provided the correct code below for you to use.
Also, I don't know why you're using a StatefulWidget for your DetailPage class since you don't seem to be doing anything that requires state management. You could easily make this a StatelessWidget but maybe there's more code that I'm unable to see.
// details page
import 'package:flutter/material.dart';
class DetailPage extends StatefulWidget {
final var documents; // created new final variable that will be initialized by the constructor
DetailPage(this.documents);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
//var _documents; // Don't use this
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: ListView.builder(
itemCount: widget.documents == null ? 0 : widget.documents.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.documents[index]['title']), // don't know why you need to specify the index since you already did in your other classs
);
}));}}
Related
Am a completely new flutter dev. I am trying to save a document from a queried firestore list on another saved documents page like an add to cart functionality. Am passing doc id as arguments to another page from firestore so that I get data based on the previous selection. Now how can I send the firestore reference and save it to the other screen without navigating to it so that users are able to save their favorite docs on another page and access them? Here is my Assignment page that lists the docs based on the previous selection.
class Assignments extends StatelessWidget {
final String programId;
final String yearId;
final String semesterId;
final String courseId;
const Assignments(
{Key key, this.programId, this.yearId, this.semesterId,
this.courseId})
: super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar2(title: 'Assigment'.toUpperCase(), ),
body: Column(
children: [
Expanded(
child: ContentArea(
addPadding: false,
child: StreamBuilder(
stream:
getAssignment(programId, yearId, semesterId, courseId),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
color: kOnSurfaceTextColorYellow),
);
}
return ListView.separated(
padding: UIParameters.screenPadding,
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
final data = snapshot.data.docs[index];
return DisplayCard(
title: data['nameOfAssignment'],
icon: Icons.add,
// Here is the action i want that should save the documment to
// the SavedPage empty list without navigating to it
onTapIconSave: (){}
onTap: () => Get.to(Pdf(
nameOfAssignment: data['nameOfAssignment'],
pdfUrl: data['pdfUrl'],
)),
);
},
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(
height: 10,
);
},
);
})),
),
],
),
);
}
}
Here is the SavedPage which may be similar to the cart page. Am not sure what to do in order to save the Document from the Assignment Page in a Dynamic growable list
class Saved extends StatefulWidget {
const Saved({ Key key }) : super(key: key);
#override
State<Saved> createState() => _SavedState();
}
class _SavedState extends State<Saved> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar2(title: 'Saved'),
body: Column(
children: [],
),
);
}
}
You can add a state management package like Provider or Bloc, also you could save your data in your local database and access them from there. I recommend Provider, easy to use, and its what you need.
im trying to use scoped model in 2 screens in the app and i dont want to run the app with scoped model, i just initialize scoped model in first screen and it worked but i got an error in the second screen where i navigate to it. so what do i do?
first i call the first screen from the pre screen like this
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ServiceDetails(model: new CartModel()),
)
);
then in ServiceDetails i didnt get any error
so in build widget
#override
Widget build(BuildContext context) {
return ScopedModel(
model: widget.model,
child: Scaffold(...)
);
}
and i have a cart button which on tap:
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => new Cart()
),
);
},
Cart class:
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Your Cart")),
body: ScopedModel.of<CartModel>(context, rebuildOnChange: true).cart.length == 0 ?
Container(
alignment: Alignment.center,
child: Text("Your cart is empty!",style: new TextStyle(color: Colors.grey))
) :
Container(
padding: EdgeInsets.only(top:15),
child: cartListView()
)
);
}
Widget cartListView(){
return ScopedModelDescendant<CartModel>(
builder: (context, child, model) {
return ListView.builder(
shrinkWrap: true,
itemCount: ScopedModel.of<CartModel>(context,rebuildOnChange: true).total,
itemBuilder: (context, index) {
return Container(
child: Image.asset(model.cart[index].fs.image)
)
}
)})}
}
so when i enter cart page i got an error
can't find the correct scoped model
anyone help plz
I have a listview where each item is one document from a firestore collection. I would like to tap the item and pass the document information to a details page.
This is how I am retrieving document information within the first stream:
child: Text(streamSnapshot.data.docs[index]['event_title'],
This is how I'm attempting to send the data to the next page:
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, EventPage.id, arguments: streamSnapshot.data.docs[index]);
},
I'm lost as to how to receive the passed data:
class _EventPageState extends State<EventPage> {
#override
final db = FirebaseFirestore.instance;
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments;
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('event_title'),
),
child: Column(
I know I need a StreamBuilder on the next page, but do you have any insight on how to make that stream show only the passed in document?
Why don't you use Provider instead? It'll help you to avoid boiler plate and as you're using streams it'll let you handle information in a better way. Check about it here
I have worked out an answer to this question. I'm sure there are several ways to do this, but here's mine:
The key is to pass the firestore document ID to the next page. In this example code, I pass streamSnapshot.data.docs[index].id.toString() as a parameter to a custom widget. I've located my named route within that widget.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('events')
.where('start_date', isGreaterThanOrEqualTo: DateTime.now())
.snapshots(),
builder: (context, AsyncSnapshot streamSnapshot) {
if (!streamSnapshot.hasData) {
return SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else
return SizedBox(
height: 250,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: streamSnapshot.data.docs.length,
itemBuilder: (ctx, index) =>
EventListHorizontalTile(
//passes the document ID as a string down to the horizontally scrollable tile,
//where we push a named route with the docID string as an argument
firestoreDocID: streamSnapshot.data.docs[index].id.toString(),
image: streamSnapshot.data.docs[index]['main_image'],
name: streamSnapshot.data.docs[index]['name'],
),
),
);
}),
I then created a class to pass as an argument through a named route.
class Events {
final String firestoreDocID;
Events({
required this.firestoreDocID,
});
}
Now, within my EventListHorizontalTile widget:
class EventListHorizontalTile extends StatelessWidget {
const EventListHorizontalTile({
Key? key,
required this.name,
this.firestoreDocID = '',
}) : super(key: key);
final String name;
final String firestoreDocID;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
//Here I am pushing a named route with an argument, using that Events class I made earlier.
Navigator.pushNamed(context, EventPage.id, arguments: Events(firestoreDocID: firestoreDocID));
},
//child: The rest of the list tile widget
),
Now we have to write a bit of code in the EventPage to receive the argument.
class EventPage extends StatefulWidget {
const EventPage({
Key? key,
}) : super(key: key);
static String id = 'EventPage';
#override
_EventPageState createState() => _EventPageState();
}
class _EventPageState extends State<EventPage> {
#override
Widget build(BuildContext context) {
//This is how we receive the argument.
final args = ModalRoute.of(context)!.settings.arguments as Events;
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//Some text to see if the string made it.
Text(args.firestoreDocID),
]),
),
);
}
}
And that's it! Once you have that document ID in your new page, you can call a Streambuilder like this:
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('events')
.doc(args.firestoreDocID)
.snapshots(),
I am new in flutter development. I cannot find my mistake. I can't see my images on my app. when I use them without slider it works what is wrong in my code can someone help me?
import 'package:feedme_start/widgets/Navigation_Drawer_Widget.dart';
import 'package:flutter/material.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:flutter_swiper/flutter_swiper.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg"
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.red,
title: Center(child: Text("FEED ME")),
actions: <Widget>[
IconButton(onPressed: () {}, icon: Icon(Icons.call))
],
),
drawer: NavigationDrawerWidget(),
backgroundColor: Colors.white,
body:
Container(
constraints: BoxConstraints.expand(height: 200),
child: imageSlider(context),
),
/*Swiper(itemCount: imageList.length,
itemBuilder: (context, index) {
return Image.network(imageList[index],/*errorBuilder:
(BuildContext context, Object exception, StackTrace? stackTrace), {return const("resim yüklenemedi")},*/
fit: BoxFit.cover,);
},)*/
),
);
}
}
Swiper imageSlider(context){
return new Swiper(
autoplay: true,
itemBuilder: (BuildContext context, int index) {
return new Image.network("https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg",fit: BoxFit.fitHeight,);
},
itemCount: 10,
viewportFraction: 0.8,
scale: 0.9,
);
}
also here is the screenshots;
when I run the program it tries to upload the image. then it sends me the this screen and shows the 'rethrow' line error:
after I continue debugging my screen looks like in the second picture:
You're simply forgetting to add , in your list.
A newline string after a string is considered as a string. For example, the following variable:
var text = "one two three"
"four five six";
is the same as:
var text = "one two three" + "four five six";
So, instead of:
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg"
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];
change to:
final imageList = [
"https://cdn.yeniakit.com.tr/images/news/625/pratik-degisik-yemek-tarifleri-en-sevilen-tarifler-h1581081558-3ff37b.jpg" ,
"https://cdn.yemek.com/mncrop/940/625/uploads/2021/04/patlicanli-pilav-yemekcom.jpg"
];
I'm new to UI Programming/ App development.
My goal is it to have a screen/page/whatever on which one can arrange different Widgets.
Of course this "layout" should be saved in some way but I have no clue how.
This is the page on which you can arrange the widgets:
const MyDesign({Key key,this.designName}):super(key: key);
final String designName;
#override
_MyDesignState createState() => _MyDesignState();
}
class _MyDesignState extends State<MyDesign> {
List<Widget> movableItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.designName),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
movableItems.add(MoveableStackItem(item: Knob(),));
});
}
),
body: Stack(
children:
movableItems,
),
);
}
}
As you can see I use a List to store my movable objects and a Stack to draw them.
Is there any possibility to store this List of widgets for a specific instance of this page and retrieve it after closing the app?
I also want to have the possibility to create multiple "layouts" heres my approach:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<MyDesign> myDesignPages = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final String dname = (await _asyncInputDialog(context)) as String;
setState(() {
myDesignPages.add(new MyDesign(designName: dname,));
});
},
child: Icon(Icons.add),
),
body:
new ListView.builder(
itemCount: myDesignPages.length,
itemBuilder: (BuildContext context, int i){
return ListTile(
title: Text(myDesignPages[i].designName),
onTap: () {
Navigator.push<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (context) => myDesignPages[i]) );
},
);
}
),
);
}
}
I assume that I'm quite off the track here. For that I'm happy places like this exist.
Thanks a lot
To answer your first question: Yes it's possible to encode and save your list content as a String and to retrieve it later by decoding the saved String. To save String as key value pair you can for example use this plugin Shared preferences. To encode and decode your list you can use respectively jsonEncode and jsonDecode from dart:convert package