class Rapidoreach extends StatefulWidget {
const Rapidoreach({
Key? key,
this.width,
this.height,
this.uid,
}) : super(key: key);
final double? width;
final double? height;
final String? uid;
#override
_RapidoreachState createState() => _RapidoreachState();
}
class _RapidoreachState extends State<Rapidoreach> {
#override
void initState() {
RapidoReach.instance.init(apiToken: 'api-key', userId: 'userid');
RapidoReach.instance.setOnRewardListener(onRapidoReachReward);
RapidoReach.instance.setRewardCenterClosed(onRapidoReachRewardCenterClosed);
RapidoReach.instance.setRewardCenterOpened(onRapidoReachRewardCenterOpened);
RapidoReach.instance
.setSurveyAvaiableListener(onRapidoReachSurveyAvailable);
super.initState();
}
void onRapidoReachReward(num quantity) {
final DocumentReference docRef = FirebaseFirestore.instance.collection("users").doc(uid); //Here is the error (uid)
docRef.update({"prizecoins": FieldValue.increment(quantity)});
}
void onRapidoReachSurveyAvailable(int? survey) {
debugPrint('ROR: $survey');
}
void onRapidoReachRewardCenterClosed() {
debugPrint('ROR: closed');
}
void onRapidoReachRewardCenterOpened() {
debugPrint('ROR: opened');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: const Text("Launch RapidoReach"),
onPressed: () => RapidoReach.instance.show(),
),
],
)),
),
);
}
}
Why do I have an issue saying that uid is not defined when is already defined in StatefulWidget?
Can someone help me?
In the state of a StatefulWidget, you can access the instance of the widget through the getter widget, so you need to replace uid with widget.uid:
void onRapidoReachReward(num quantity) {
final DocumentReference docRef =
FirebaseFirestore.instance.collection("users").doc(widget.uid);
docRef.update({"prizecoins": FieldValue.increment(quantity)});
}
you can get the properties from the StatefulWidget inside the State Object by assigning the widget keyword at first :
widget.uid
in your case replace with this :
FirebaseFirestore.instance.collection("users").doc(widget.uid);
Related
I've got a List<Data> which is diplayed in a ListView that uses Riverpod to watch any changes to the list. When I add or remove an item from that list, the ListView rebuilds as intended, but it appears like every ListViewItem and its descending widgets are rebuild - even though they show the same content as before. Here's a simplified version of my code:
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final listLength = ref.watch(dataLengthProvider);
return MaterialApp(
home: Scaffold(
body: Column(
children: [
ElevatedButton(
child: const Icon(Icons.add),
onPressed: () => ref.read(dataListProvider.notifier).add(),
),
Expanded(
child: ListView.builder(
itemCount: listLength,
itemBuilder: (context, index) {
return MyListItem(index);
},
),
),
],
),
),
);
}
}
class MyListItem extends ConsumerWidget {
final int index;
const MyListItem(this.index, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final countValue =
ref.watch(dataItemProvider(index).select((dataItem) => dataItem.value));
return Text('Value: ${countValue.toString()}');
}
}
// Providers -------------------------------------------------------------------
final dataListProvider = StateNotifierProvider<DataListNotifier, List<Data>>(
(ref) => DataListNotifier());
final dataLengthProvider =
Provider<int>((ref) => ref.watch(dataListProvider).length);
final dataItemProvider = Provider.family<Data, int>(
(ref, index) => ref.watch(dataListProvider)[index]);
// Notifier --------------------------------------------------------------------
class DataListNotifier extends StateNotifier<List<Data>> {
DataListNotifier() : super([const Data(), const Data()]);
void add() {
state = [...state, const Data(value: 0)];
}
}
// Data model ------------------------------------------------------------------
#immutable
class Data {
final int value;
const Data({this.value = 0});
Data copyWith({int? newValue}) => Data(value: newValue ?? value);
}
Now my question: Is Flutter smart enough to automatically re-use those unchanged widgets?
If not, what can I do to avoid unneccessary builds?
You can check something. To do this, remake your class MyListItem in to have access to dispose():
class MyListItem extends ConsumerStatefulWidget {
final int index;
const MyListItem(
this.index, {
Key? key,
}) : super(key: key);
#override
ConsumerState createState() => _MyListItemState();
}
class _MyListItemState extends ConsumerState<MyListItem> {
#override
Widget build(BuildContext context) {
print(widget.index);
final countValue = ref.watch(
dataItemProvider(widget.index).select((dataItem) => dataItem.value));
return Text('Value: ${countValue.toString()}');
}
#override
void dispose() {
print('dispose: ${widget.index}');
super.dispose();
}
}
and add method delete() near add():
void delete() {
state.removeLast();
state = List.of(state);
}
and add button in MyApp:
ElevatedButton(
child: const Icon(Icons.delete),
onPressed: () => ref.read(dataListProvider.notifier).delete(),
),
And check this code again. There, of course, the RangeError (index) error will be raised, but this is not the point. But on the other hand, you can see that the dispose() method is not called when the element is added, which means that the object is not removed from the tree. At the same time, when the last element is removed, we can see the call to the dispose() method, but only for the last element! So you are on the right track :)
You can use the select for getting the reference of the provider for stopping unnecessary rebuilds in the list item.
https://riverpod.dev/docs/concepts/reading/#using-select-to-filter-rebuilds
I have created a function to get a random element from a list. It works perfectly in dartpad but when I put it into my Flutter app I get the following error:
Closure: (List<dynamic>) => dynamic from Function 'player1': static.
The contents of the dart file:
import 'package:flutter/material.dart';
import 'dart:math';
List<String> players = ["Alice", "Jane", "Susan"];
String tempPlayer = "";
player1(List players) {
//players[Random().nextInt(players.length)];
String tempPlayer = players[Random().nextInt(players.length)];
return tempPlayer;
}
class RandPlay extends StatefulWidget {
const RandPlay({Key? key}) : super(key: key);
#override
State<RandPlay> createState() => _RandPlayState();
}
class _RandPlayState extends State<RandPlay> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
print(player1);
});
},
child: Text("Random")),
Text("$player1"),
],
),
);
}
}
I am new to this and I am really confused... I have tried searching for similar situations to no avail. Assistance would be greatly appreciated.
player1 is a method, expect a list.
You can do it like
List<String> players = ["Alice", "Jane", "Susan"];
String player1(players) {
String tempPlayer = players[Random().nextInt(players.length)];
return tempPlayer;
}
class RandPlay extends StatefulWidget {
const RandPlay({Key? key}) : super(key: key);
#override
State<RandPlay> createState() => _RandPlayState();
}
class _RandPlayState extends State<RandPlay> {
late String tempPlayer = player1(players);
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
ElevatedButton(
onPressed: () {
tempPlayer = player1(players);
setState(() {});
},
child: Text("Random")),
Text("$tempPlayer"),
],
),
);
}
}
I've only been coding in Flutter for a few weeks now and I would like to know if it is possible just to navigate to a page using named routes that has received arguments from another page? The main objective is to navigate to the Cart Screen from two different pages where one passes an argument while the other doesn't. Here is my code below to explain my question:
This is the first part of the code which navigates to the cart screen after passing arguments id and quantity
class ItemDetailsState extends State<ItemDetails> {
int quantity = 1; //quantity
#override
Widget build(BuildContext context) {
final routes =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
final id = routes["id"]; //id
return Scaffold(
......
InkWell(
onTap: () {
Navigator.of(context).pushNamed('/cart-screen', arguments: { //This navigates to the cart screen passing arguments id and quantity
'id': routes["id"],
'quantity': quantity,
});
Provider.of<CartItemProvider>(context, listen: false)
.addItems(id, name, restaurantName, price, quantity);
},
);
}
}
This is the Cart Screen that receives the arguments and filters data from a Provider Class:
class CartScreen extends State<CartScreenState> {
#override
Widget build(BuildContext context) {
final routes =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
final id = routes['id']; //Received Arguments
final quantity = routes['quantity']; //Received Arguments
final provider =
Provider.of<PopularDishesProvider>(context).getProductById(id); //Provider that filters the data as per ID
My idea is to navigate to the Cart Screen page from another page like this but it throws the below error:
class HomeScreenState extends State<HomeScreen> {
Widget build(BuildContext context) {
return Scaffold(
..............
body: Row(
children: [
InkWell(
onTap: () => Navigator.of(context)
.pushReplacementNamed('/cart-screen'), //Navigate to the Cart Screen
child: const Icon(
Icons.shopping_cart_outlined,
color: Colors.grey,
size: 30,
),
),
InkWell(
onTap: () {},
child: const Icon(
Icons.notifications_none_outlined,
color: Colors.grey,
size: 30,
),
)
],
)
The method '[]' was called on null.
Receiver: null
Tried calling: []("id")
The above error I believe is owing to the fact that I'm trying to just navigate to '/cart-screen' without passing any argument in the HomeScreenState widget. I need suggestions to know if there's any way to get around this?
The route is declared in the main.dart file as it should like
routes : {
'/cart-screen': (context) => CartScreen(),
}
You can check null value using
#override
Widget build(BuildContext context) {
var arguments3 = ModalRoute.of(context)!.settings.arguments;
var routes=
arguments3!=null? arguments3 as Map<String, dynamic>:{};
final id = routes['id']??0; //Received Arguments
final quantity = routes['quantity']??0; //Received Arguments
final provider =
Provider.of<PopularDishesProvider>(context).getProductById(id);
We can pass argument with the help of argument property in pushnamed method
Navigator.pushNamed(context, AppRoutes.Page1,
arguments: {"name": "lava", "body": "chi"});
Receive value
var arguments3 = ModalRoute.of(context)!.settings.arguments;
var arguments2 =
arguments3!=null? arguments3 as Map<String, dynamic>:{};
May like this
SAmple Code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: "/",
routes: {
AppRoutes.home: (context) => Home(),
AppRoutes.Page1: (context) => Page1(),
},
title: _title,
// home: ,
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("title")),
body: const Center(
child: MyStatelessWidget(),
),
);
}
}
var _color = Colors.black;
var _value = 0.0;
class MyStatelessWidget extends StatefulWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
State<MyStatelessWidget> createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, AppRoutes.Page1);
},
child: Text("Without Argument")),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, AppRoutes.Page1,
arguments: {"name": "lava", "body": "chi"});
},
child: Text("With Argument")),
],
),
);
}
#override
void initState() {}
}
class Page1 extends StatelessWidget {
const Page1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var arguments3 = ModalRoute.of(context)!.settings.arguments;
var arguments2 =
arguments3!=null? arguments3 as Map<String, dynamic>:{};
// {"name": "nodata", "body": "no data"};
return Material(
child: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(arguments2["name"] ?? "Nodata",
style: TextStyle(fontSize: 30)),
Text(
arguments2["body"] ?? "No DAta",
style: TextStyle(fontSize: 30),
),
],
),
),
),
);
}
}
class AppRoutes {
static String failed = "/page2";
static String Page1 = "/page1";
static String home = "/";
}
your design is a little confusing.
if you are trying to get the ID and Quantity in the Cart-screen, then why do you want to navigate to it without the arguments?
any how, I guess you have a use case where you want to do different thing if the arguments are not passed. then the only thing you need is to check if the arguments are null. right?
#override
Widget build(BuildContext context) {
final routes =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
if (routes != null) {
final id = routes['id']; //Received Arguments
final quantity = routes['quantity']; //Received Arguments
final provider =
Provider.of<PopularDishesProvider>(context).getProductById(id);
} else {
// do the things here when no argument is passed.
}
I have a list of event name in a stateful widget like this
main.dart
class Fav extends StatefulWidget {
#override
_FavState createState() => _FavState();
}
class _FavState extends State<Fav> {
final PageController ctrl = PageController(viewportFraction: 0.8);
final Firestore db = Firestore.instance;
Stream slides;
var fav = ['3-Tech Event'];
.
.
.
And on another page, I want to add a string, let's say,
'5-Art Exhibit'
into the
var fav = ['3-Tech Event'];
to get the final result
fav = ['3-Tech Event', '5-Art Exhibit'];
on the page above. How do I do that? Here's my code for the button
Event.dart
class Star extends StatefulWidget {
#override
_StarState createState() => _StarState();
}
class _StarState extends State<Star> {
Color _iconColor = Colors.grey;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(55.0),
child: Transform.scale(
scale: 2.0,
child: IconButton(
icon: Icon(
Icons.star,
color: _iconColor,
),
onPressed: () {
setState(() {
_iconColor = (_iconColor == Colors.yellow) ? Colors.grey : Colors.yellow;
});
})
),
);
}
}
Thank you in advance!
UPDATE
I followed #Viren V Varasadiya advice and updated my code to this
main.dart
class Fav extends StatefulWidget {
#override
_FavState createState() => _FavState();
}
class _FavState extends State<Fav> {
var fav = ['3-Tech Event'];
updatedata(String item) {
setState(() {
fav.add(item);
});
}
And on the other file, I removed Star class (because it's intended to be used in another class anyway) and it looked like this
class Event extends StatefulWidget {
final eventInfo;
Event({Key key, List eventInfo}) //I have to pass a list of data to this
: this.eventInfo = eventInfo, //page from another class
super(key: key);
final Function updatedata;
Event.addToFavWith({this.updatedata});
#override
_EventState createState() => _EventState();
}
class _EventState extends State<Event> {
Color _iconColor = Colors.grey;
#override
Widget build(BuildContext context) {
.
.
.
Container( //This used to be Container(child:Star())
child: InkWell(
child: Container(
padding: EdgeInsets.all(55.0),
child: Transform.scale(
scale: 2.0,
child: IconButton(
icon: Icon(
Icons.star,
color: _iconColor,
),
onPressed: () {
setState(() {
_iconColor = (_iconColor == Colors.yellow)
? Colors.grey
: Colors.yellow;
widget.updatedata(name);
});
})),
),
)),
And now I get a couple of errors.
All final variables must be initialized, but 'eventInfo' is not. Try
adding an initializer for the field.
All final variables must be initialized, but 'updatedata' is not. Try
adding an initializer for the field.
You have to create a function in parent widget and pass it to child widget and call it in child widget, we work for you.
Following minimal code help you more.
class DeleteWidget extends StatefulWidget {
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
var fav = ['3-Tech Event'];
updatedata(String item) {
setState(() {
fav.add(item);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(fav.toString()),
Star(
updatedata: updatedata,
),
],
)));
}
}
class Star extends StatefulWidget {
final Function updatedata;
Star({this.updatedata});
#override
_StarState createState() => _StarState();
}
class _StarState extends State<Star> {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text("add item"),
onPressed: () {
widget.updatedata('5-Art Exhibit');
}),
);
}
}
Update:
There is no need to create named constructor.
class Event extends StatefulWidget {
final eventInfo;
Event({Key key, List eventInfo, this.updatedata}) //I have to pass a list of data to this
: this.eventInfo = eventInfo, //page from another class
super(key: key);
final Function updatedata;
#override
_EventState createState() => _EventState();
}
I have a list of choice widget and want to pass the selected choice to another widget.
Here is the list of choice widget
class ChoiceChipWidget extends StatefulWidget {
final List<String> reportList;
final Function(String item) onChoiceSelected;
ChoiceChipWidget(this.reportList, this.onChoiceSelected);
#override
_ChoiceChipWidgetState createState() => new _ChoiceChipWidgetState();
}
class _ChoiceChipWidgetState extends State<ChoiceChipWidget> {
String selectedChoice = "";
_buildChoiceList() {
List<Widget> choices = List();
widget.reportList.forEach((item) {
choices.add(Container(
child: ChoiceChip(
label: Text(item),
selected: selectedChoice == item,
onSelected: (selected) {
setState(() {
selectedChoice = item;
widget.onChoiceSelected(item);
print(selectedChoice); //DATA THAT NEEDS TO BE PASSED
});
},
),
));
});
return choices;
}
#override
Widget build(BuildContext context) {
return Wrap(
children: _buildChoiceList(),
);
}
}
I need to pass it to this widget
class AddCashPage extends StatefulWidget {
#override
_AddCashPageState createState() => _AddCashPageState();
}
class _AddCashPageState extends State<AddCashPage> {
void createTodo() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
String repetition = //DATA NEEDS TO GO HERE;
final addCash = AddCash(repetition);
setState(() {
id = addCash.id;
});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
ChoiceChipWidget(chipList, (item) {
selectedItem = item;
}),
],
),
RaisedButton(
child: Text("Update Cash Flow"),
onPressed: createTodo,
),
],
),
),
);
}
}
I tried making a constructor inside AddCashPage
like this
class AddCashPage extends StatefulWidget {
final ChoiceChipWidget choiceChipWidget;
AddCashPage({Key key, #required this.choiceChipWidget}) : super(key: key);
#override
_AddCashPageState createState() => _AddCashPageState();
}
I think you just missed to call setState() in here:
ChoiceChipWidget(chipList, (item) {
selectedItem = item;
}),
Like this:
ChoiceChipWidget(chipList, (item) {
setState(() => selectedItem = item);
}),
Then you could do this:
AddCash(selectedItem)
Make sure to declare the selectedItem variable inside _AddCashPageState, I don't see it on your code.
Your choice widget passes the data to the AddCashPage via the constructor you created, but you're missing something. You need to pass the data that AddCashPage has to its state (_AddCashState) so that you can use it there. Basically, you need to create one more constructor.
class AddCashPage extends StatefulWidget {
final ChoiceChipWidget choiceChipWidget;
AddCashPage({Key key, #required this.choiceChipWidget}) : super(key: key);
#override
_AddCashPageState createState() => _AddCashPageState(choiceChipWidget: choiceChipWidget);
}
And in _AddCashPageState:
class _AddCashPageState extends State<AddCashPage> {
final choiceChipWidget;
_AddCashPageState({Key key, #required this.choiceChipWidget});
}
To use your passed data inside _AddCashPageState class you can use widget property of the corresponding state of the related Stateful class.
For Ex : To use choice chip widget in your class you can use it like widget.ChoiceChipWidget
Any properties/methods provided in AddCashPage class can be accessed in its State class _AddCashPageState() using widget.ChoiceChipWidget property;
You can use this widget property inside methods only like, initState(), build(), dispose() etc.