How do I pass the class as a parameter in flutter? - flutter

I am developing an Android application on flutter, where I want to pass class as a parameter. I am a page where all the food recipes are shown and the data comes from database. When any of the recipe is clicked than new screen will appear and user will see the details of that recipe Now what is want is that when normal user click the recipe the user will move to normal screen where edit/delete option is not present and when Admin click the screen than he will move to the screen where edit/delete option is present
class RecipeCard extends StatelessWidget {
final String title;
final String rating;
final String cookTime;
final String thumbnailUrl;
const RecipeCard({
super.key,
required this.title,
required this.cookTime,
required this.rating,
required this.thumbnailUrl,
});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: const Duration(seconds: 1),
transitionsBuilder: (context, animation, animationTime, child) {
animation = CurvedAnimation(
parent: animation, curve: Curves.fastLinearToSlowEaseIn);
return ScaleTransition(
scale: animation,
alignment: Alignment.center,
child: child,
);
},
pageBuilder: (context, animation, animationTime) {
return UpdateOrDeleteRecipe(
title: title,
cookTime: cookTime,
rating: rating,
thumbnailUrl: thumbnailUrl,
);
},
),
);
},
child: Container(...),
);
}
}
Now in the above code, in return InkWell( onTap: () {I want to pass the class name so that when the card is clicked it will move to the class I want it to move.
How can I do that? Remember when I move to the next page the following argument will also move to the next page.
return UpdateOrDeleteRecipe(
title: title,
cookTime: cookTime,
rating: rating,
thumbnailUrl: thumbnailUrl,
);

If you have two separate screens for Admin and User you can put a condition like isAdmin ? UpdateOrDeleteRecipe(...) : Detail() in pageBuilder, Or you can make a single screen for both Admin & User and put bool isAdmin argument on the screen arguments, when Admin enters that screen the isAdmin will be true and accordingly, you can show edit/delete options and is isAdmin is false don't show them.

Related

Having trouble to build single pages for the items on my AlphabetS collage

I'm new on flutter. I'm working on app that I want to publish and it will my first as beginner. I built an AlphabetScrollPage in a Scaffold with 56 items in it. My goal is to have a single pages describing all item individually. Like when to click on one it automatically directly you to a new page.
I'm actually having a snackBar when you click on one it's comes with a Text saying you 'clicked on this $item'. Any help will be beneficial, thank you![enter image description here][1]
You need to pass the details of the item to the page which describes the item
this is the one simple way you can do it
it isn't the best way but it works for small apps
List items = [];
for (var i = 0; i < 56; i++) {
items.add(MyItem('id $i', 'name $i'));
}
//this is you list view
ListView.builder(
itemCount: 56,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailsScreen(id: items[index].id,itemName: items[index].name,))); //this should be item id
},
child: ListTile(
title: Text('Item ${index + 1}'),
),
);
this is the item class it's very simple
class MyItem {
final String id;
final String name;
MyItem(this.id, this.name);
}
and describing page
class DetailsScreen extends StatelessWidget {
final id;
final itemName;
const DetailsScreen({Key? key, required this.id,required this.itemName}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Text(itemName),
),
),
);
}
}
you can do it also by using Navigate with arguments then use this id to get the item details
if your app is big you should use state management package
like provider, it is very easy and useful

How to pass a Firestore document from a stream to next page in Flutter?

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(),

How to select only one item in ListView and render it?

I started studying flutter and I'm having a doubt about LsitViewBuilder.
I have this ListView that accesses the JSON data locally by rootBundle, but I would like that when I click on some item it would only open it on the second page.
I wanted so much to know how you can select.
My ListView
List<dynamic> buy;
#override
void initState() {
super.initState();
rootBundle.loadString('assets/dados.json').then((jsonData) {
this.setState(() {
buy = jsonDecode(jsonData);
});
});
}
........
ListView.builder(
itemCount: buy?.length ?? 0,
itemBuilder: (_, index) {
return buildCardBuy(context, index, buy);
}
),
You can wrap your list view item with the GestureDetector widget, making the Tap event to navigate to another page with the item tapped.
ListView.builder(
itemCount: buy?.length ?? 0,
itemBuilder: (_, index) {
return GestureDetector(
child: buildCardBuy(context, index, buy),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
// the first refeers to the property on your detail DetailScreen
// and the second refers to the current buy being render on
// this list view builder
builder: (context) => DetailScreen(buy: buy),
),
);
);
}
),
And in your DetailScreen something like
class DetailScreen extends StatelessWidget {
final dynamic buy;
DetailScreen({Key key, #required this.buy}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: Container()
);
}
}
I would build a second widget (let's call it ItemWidget) which represents the detail page for the object you want to 'open'.
Then I would add to that ItemWidget a property for the object data that you need to pass.
After that I would implement the logic so that when the list item is clicked, it switches the current list widget with a new ItemWidget and passes to it the properties of the clicked object.

provider view model with collection of objects

I have a state model that has collection of objects that have a collection of objects inside as well (think collection of todo lists):
class StateModel extends ChangeNotifier {
List<TodoList> todoLists;
}
simplified TodoList class looks like this:
class TodoList {
int id;
List<Item> items;
}
class Item {
int id;
String name;
bool status; // true is done, false is not done
}
Now one of my Views is ListView of my todo lists (where I display only name) and that's east. But I want to have a todo list detail view (where data of single todo list is displayed) where I want to mark todo items as done (i.e. set their status to true). How should I do it? I could have method in StateModel which would find a TodoList object by id, then mark items as done. This could look something like this:
class StateModel extends ChangeNotifier {
// (...)
void markItemAsDone(listId, itemId) {
// find todo list in StateModel
// find item in given list
// mark it as done
// notifyListeners()
}
}
But this seems wrong. What I would like to have is a way to get TodoList object view model and use its methods, not StateModel methods. How should I approach this? Can I have another view model (TodoListState), and have a collection of TodoListState objects in StateModel? Is this a use case for ProxyProvider?
I hope my question is clear, let me know if this needs more explanation.
My way is when you the pop back from the detail view, you also transfer a value:
I await for the value from the pop back then you do something with the value. Here is my code
#override
Widget build(BuildContext context) {
WordModel provider = Provider.of<WordModel>(context);
return Consumer<WordModel>(
builder: (BuildContext context, value, Widget child) => InkWell(
onTap: () async {
dynamic status = await Navigator.of(context).push(PageRouteBuilder<ListFavouriteWord>(
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return ListFavouriteWord(value);
}));
//do something with markedValue
//provider.update(status)
},
child: Padding(
padding: const EdgeInsets.only(top: 16.0, right: 16.0),
child: Badge(
child: Icon(Icons.face),
badgeContent: Text(
"${value.counter}",
style: Theme.of(context)
.textTheme
.button
.copyWith(color: Colors.white, fontSize: 10.0),
),
),
),
),
);
}
I have found another way to do it. You just need:
ChangeNotifierProvider.value() to use your provider in your previous route
My code:
onTap: () async {
dynamic markedValue = await Navigator.of(context).push(
PageRouteBuilder<ListFavouriteWord>(
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return ChangeNotifierProvider.value(
value: value,
child: ListFavouriteWord(),
);
}));
}

Resize scaffold in flutter

Project
Hi, I'm trying to make some custom transition in flutter between two simple screen. My goal is to use Navigator.push(context, MyRoute(..)) to call another the second screen on top of the first one. My problem is that I want the second screen to be only half size of the height of the device. The rest of the screen should only display the old page, maybe with some kind of blur.
I'm searching for a BottomSheet style effect but without using the actual widget.
Problem
No matter what I try, when Navigator.push is called, the new screen will always be resized to fill the entire screen and I'm unable to get a smaller scaffold on top with some transparency to hide the old page.
Thanks
I think you can do something like the following. First create a transparent page route. Here is a class that extends the PageRoute class to create transparent page route so you can see what is behind it. It overrides the "opaque" value and sets it to false.
import 'package:flutter/widgets.dart';
/// Creates a route that leaves the background behind it transparent
///
///
class TransparentRoute extends PageRoute<void> {
TransparentRoute({
#required this.builder,
RouteSettings settings,
}) : assert(builder != null),
super(settings: settings, fullscreenDialog: false);
final WidgetBuilder builder;
#override
bool get opaque => false;
#override
Color get barrierColor => null;
#override
String get barrierLabel => null;
#override
bool get maintainState => true;
#override
Duration get transitionDuration => Duration(milliseconds: 350);
#override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
final result = builder(context);
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(animation),
child: Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: result,
),
);
}
}
Use that as your page route.
In the Scaffold that you want to navigate to you can do the following. This will make it so it only takes up half of the screen and shows the previous page behind it:
Navigator.of(context).push(
TransparentRoute(
builder: (context) => Scaffold(
appBar: null,
backgroundColor: Colors.transparent,
body: Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height / 2,
color: Colors.red,
),
),
)
),
);
If I understand your problem correctly I think this should do the trick!