Flutter call setState() to update UI in another class - android

I am trying to call a setState when a button is pressed so the ui can show the new list but even using functions i cant use setState or it will give me the error saying im calling setState inside a constructor.
This is my code for the statlessWidget:
class _MessageCard extends StatelessWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
_MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 600,
child: InkWell(
child: Container(
width: 900,
color: Colors.grey[200],
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Center(
child: Container(
width: 600,
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
child: Icon(
Icons.notifications,
color: Colors.red[400],
),
backgroundColor: Colors.grey[200],
),
title: Text(
(this.message.vDescricao ?? '').trim(),
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
subtitle: Text(
(this.message.vData ?? '').trim() +
' ' +
(this.message.vHora ?? '').trim(),
style: TextStyle(
color: Colors.red[400],
fontSize: 13,
),
),
trailing: FlatButton(
child: Text(
Translations.of(context)
.trans('finishmessageshort'),
),
onPressed: () => _showDeleteAlertMessage(
this.message.vNumero, context)),
),
Divider(
color: Colors.black54,
),
],
),
),
),
),
),
),
));
}
Future _showDeleteAlertMessage(String id, BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(
Translations.of(context).trans('finishmessage') + '?',
),
actions: <Widget>[
FlatButton(
child: new Text(
Translations.of(context).trans('closealert'),
),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: new Text(("Ok")),
onPressed: () =>
{_deleteMessage(id), Navigator.of(context).pop()},
)
],
);
});
}
_deleteMessage(String id) async {
for (i = 0; i < this.messageLenght; i++) {
if (this.messageList[0].vNumero == this.message.vNumero) {
this.messageList.removeAt(i);
_HomePageState().mensagemRepo.confirmMessage(this.message.vNumero);
await _HomePageState()._getMessages();
return this.messageList;
}
}
}
}
And this is my _getMessages()
_getMessages() async {
setState(() {
_loading = true;
_errorMsg = '';
});
try {
_messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
} catch (e) {
_errorMsg = e.toString();
}
setState(() {
_loading = false;
});
}
How can i make it so i can use this setState?
Thank you for your time and attention
Edit: Now updates List but not UI, because im not able to set HomePage state from MessageCard

You can only use setState in a StatefulWidget.
class MessageCard extends StatefulWidget {
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}

Well, you can't set value for something that doesn't exist. Stateless by name itself makes it clear that it can't hold any state. Changing the widget to a stateful widget would work.

Stateless widget can not change the state once its rendered. To use setState and re-render the widget StatefulWidget is used.
Just change your MessageCard from Stateless Widget to StatefulWidget
class MessageCard extends StatefulWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Also, now "to use your MessageCard properties" like message, messageLenght, messageList, in _MessageCardState you have to use a property like widget.message, widget.messageList and widget.messageLenght respectively.

can this work to refresh the ui?
_getMessages() async {
_HomePageState()._messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
setState(() {
_HomePageState()._messagesList();
});
}
The code for _messagesList() is:
SliverChildBuilderDelegate _messagesList() {
int count() {
if (_errorMsg != '')
return 1;
else
return _messages == null ? 0 : _messages.length;
}
return SliverChildBuilderDelegate(
(BuildContext context, int index) {
print("i: $index");
if (_errorMsg != '') {
return Padding(
padding: EdgeInsets.all(20),
child: ErrorMessage(
error: _errorMsg,
),
);
} else {
return _MessageCard(
message: this._messages[index],
messageLength: this._messages.length,
messageList: this._messages);
}
},
childCount: count(),
);
}

I managed to make it work, by making both classes into one and calling a function to draw the messagecards, thank you all for your help and attention

Related

Failed assertion: line 4021 pos 12: 'name != null': is not true

Hello programmers I have the following problem
Failed assertion: line 4021 pos 12: 'name != null': is not true.
when I use onTap(){_con.goToPage} Clicking on the image takes the user to another page
I need help please, this error has stalled my process
I show the two Dart files involved in the error
class RolesPage extends StatefulWidget {
const RolesPage({Key key}) : super(key: key);
#override
_RolesPageState createState() => _RolesPageState();
}
class _RolesPageState extends State<RolesPage> {
RolesController _con = new RolesController();
#override
void initState() {
// TODO: implement initState
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
_con.init(context, refresh);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("select a role"),
),
body: Container(
margin: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.14),
child: ListView(
children: _con.user != null ? _con.user.roles.map((Rol rol){
return _cardRol(rol);
}).toList() : []
),
),
);
}
Widget _cardRol(Rol rol){
return GestureDetector(
onTap: (){
_con.goToPage(rol.route);
},
child: Column(
children: [
Container(
height: 100,
child: FadeInImage(
image: rol.image != null
? NetworkImage(rol.image)
: AssetImage('assets/img/enchufe.jpg'),
fit: BoxFit.contain,
fadeInDuration: Duration(milliseconds: 50),
placeholder: AssetImage('assets/img/enchufe.jpg'),
),
),
SizedBox(height: 15),
Text(
rol.name ?? '',
style: TextStyle(
fontSize: 16,
color: Colors.black
),
),
SizedBox(height: 25),
],
),
);
}
void refresh(){
setState(() {
});
}
}
class RolesController {
BuildContext context;
Function refresh;
User user;
SharedPref sharedPref = new SharedPref();
Future init(BuildContext context, Function refresh) async{
this.context = context;
this.refresh = refresh;
user = User.fromJson(await sharedPref.read('user'));
refresh();
}
void goToPage(String route) {
Navigator.pushNamedAndRemoveUntil( context, route, (route) => false);
}
}
using the debug mode I realized that the route arrives null

How To Update A Widget from Another Widget with A DropDown in Flutter and Dart?

I have two separate stateful widgets. One is a dropdown with values and one is a widget I would like to update onChange of the DropDown. My issue is that the second widget does not update whenever the dropdown is changed. Below is my full code.
Dropdown Widget
import 'package:flutter/material.dart';
class DropDownList extends StatefulWidget {
List<String> values;
String select;
DropDownList({
Key key,
this.values,
this.select,
}) : super(key: key);
#override
_DropDownListState createState() => _DropDownListState();
}
class _DropDownListState extends State<DropDownList> {
List<String> dropdownValues = [
"2021",
"2020",
"2019",
"2018",
];
var selectedValue = '2021';
#override
void initState() {
setState(() {
widget.values = dropdownValues;
widget.select = selectedValue;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: DropdownButton<String>(
value: selectedValue,
icon: SizedBox.shrink(),
// iconSize: 24,
elevation: 0,
style: TextStyle(color: Colors.white),
selectedItemBuilder: (BuildContext context) {
return dropdownValues.map((String value) {
return Text(
value,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
);
}).toList();
},
underline: SizedBox.shrink(),
onChanged: (String newValue) {
setState(() {
selectedValue = newValue;
});
showJojo(selectedValue);
print(selectedValue);
},
items: widget.values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
'${value}',
style: TextStyle(
color: Colors.deepPurple,
),
),
);
}).toList(),
),
);
}
static showJojo(String select) {
return Jojo(
select: select,
);
}
}
Second Widget To get the data
class _JojoState extends State<Jojo> {
String select;
_JojoState(this.select);
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: new Text(
'${this.select}',
style: TextStyle(color: Colors.white),
),
);
}
}
Can someone point me in the right direction, please?
Amicably
Chris
You need to add didChangeDependencies() method here, here if that variable will change page will be compiled.
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
if (selectedValue != null) return;
});
}```

How to Create a List of Categories using Containers

so i wanted to create a list of categories (Day-Week-Month) and i achieved it just fine using a
listView.builder but it isn't centered on the horizontal axis so i had the idea to do it with containers.
https://im3.ezgif.com/tmp/ezgif-3-aba2cbc290ae.gif
so using only containers as the gif shows when i press it stays active even if i press on another one
which is something i dont want obviously.
https://im3.ezgif.com/tmp/ezgif-3-e1e304256aaf.gif
my code :
class DWM extends StatefulWidget {
#override
_DWMState createState() => _DWMState();
}
class _DWMState extends State<DWM> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextDWM(
text: "Day",
),
TextDWM(
text: "Week",
),
TextDWM(
text: "Month",
),
],
),
);
}
}
class TextDWM extends StatefulWidget {
final String text;
bool isActive;
TextDWM({this.text,this.isActive = false});
#override
_TextDWMState createState() => _TextDWMState();
}
class _TextDWMState extends State<TextDWM> {
#override
Widget build(BuildContext context) {
return Column(
children: [
GestureDetector(
onTap: (){
setState(() {
widget.isActive = true;
});
},
child: Text(
widget.text,
style: TextStyle(
color: widget.isActive ? Colors.white : Colors.grey,
),
),
),
SizedBox(height: 5,),
Container(
height: 2,
width: 40,
color: widget.isActive ? Colors.deepOrange : Colors.transparent,
)
],
);
}
}
Please check out the code below to learn one of the ways this could be done. However it is advisable to use something like Inherited widget or Provider to pass data down the widget tree.
import 'package:flutter/material.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
home: MyApp()));
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Flutter Demo"),
),
body: DWM(),
);
}
}
class DWM extends StatefulWidget {
#override
_DWMState createState() => _DWMState();
}
class _DWMState extends State<DWM> {
final List<String> _items = ["Day", "Week", "Month"];
List<bool> _active = []; //[true,false,false];
#override
void initState() {
super.initState();
_active = List.filled(_items.length, false);
}
void setActive(int active) {
setState(() {
_active = List.filled(_items.length, false);
_active[active] = true;
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(
_items.length,
(index) => TextDWM(
text: _items[index],
isActive: _active[index],
setActive: () => setActive(index),
),
),
),
);
}
}
class TextDWM extends StatelessWidget {
final String text;
final bool isActive;
final Function setActive;
const TextDWM({this.text, this.isActive, this.setActive});
#override
Widget build(BuildContext context) {
return Column(
children: [
GestureDetector(
onTap: () {
setActive();
},
child: Text(
text,
style: TextStyle(
color: isActive ? Colors.white : Colors.grey,
),
),
),
const SizedBox(
height: 5,
),
Container(
height: 2,
width: 40,
color: isActive ? Colors.deepOrange : Colors.transparent,
)
],
);
}
}

Flutter CallBack Function

I have a quantity that needs to be updated in the parent widget. Quantity needs to be updated when pressing + or - Icon in the child widget. I passed the callback function the the child stateless widget, but it is not working. Instead I get an error saying setstate() or markneedsbuild() called during build.
This is the parent widget
class Wash extends StatefulWidget {
#override
_WashState createState() => _WashState();
}
class _WashState extends State<Wash> {
int quantity = 0;
void updateQuantity(command) {
if (command == 'add') {
setState(() {
quantity++;
});
} else {
setState(() {
quantity--;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: OrderTile(
imgPath: 'shorts',
itemType: 'Shorts',
quantityCallBack: updateQuantity,
),
);
}
This is the child widget
class OrderTile extends StatelessWidget {
OrderTile({this.itemType, this.imgPath, this.quantityCallBack});
final String imgPath;
final String itemType;
final Function quantityCallBack;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(12.0),
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: CircleAvatar(
backgroundImage: AssetImage('images/${imgPath}.jpg'),
radius: 30.0,
),
),
Expanded(
flex: 3,
child: _Description(
title: itemType,
),
),
GestureDetector(
onTap: quantityCallBack('add'),
child: Icon(
Icons.add,
size: 24.0,
),
),
SizedBox(
width: 14,
),
Text('1'),
SizedBox(
width: 14,
),
GestureDetector(
onTap: quantityCallBack('remove'),
child: Icon(
Icons.remove,
size: 24.0,
),
),
],
),
);
}
}
Am I doing the right thing for the function call back implementation?
You're calling your callback function in the wrong way inside your onTap callback. Change:
onTap: quantityCallBack('add'),
for
onTap: () => quantityCallBack('add'),
You can only pass a function the way you passed if they have the same type. In this case the onTap is void function() it doesn't have any arguments.
Also, your not passing the updated quantity value to your Text Widget

how to call the method in provider inside the build method in flutter?

i have a method in the class which extends changeNotifier which gets the data from API. now I want to call this method whenever page is opened in the build method but when I call that method it is repeatedly called because of the notifyListeners method. I want to know how to call method only once.
ReportProvider.dart
class ReportProvider extends ChangeNotifier
{
static DateFormat dateFormat = new DateFormat('dd-MM-yyyy');
static DateFormat actualDateFormat = new DateFormat("yyyy-MM-dd");
String _toDate = dateFormat.format(new DateTime.now());
String _actualToDate = actualDateFormat.format(new DateTime.now());
String _actualFromDate = actualDateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
String _fromDate = dateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
bool _progressStatuc = false;
bool _chartVisible = true;
bool _errorVisible = false;
String _errorMessage;
String get errorMessage => _errorMessage;
bool get errorVisible => _errorVisible;
bool get chartVisible => _chartVisible;
bool get progressStatus => _progressStatuc;
String get toDate => _toDate;
String get fromDate => _fromDate;
List<PieData> _data = new List();
List<PieData> get data => _data;
Future<void> getReportData() async
{
Map<String,dynamic> sessiondata = await new Utilities().getSessionData();
int shopid = sessiondata['shopid'];
Map<String,String> reportData = new Map();
reportData['shopid'] = shopid.toString();
reportData["fromdate"] = _actualFromDate;
reportData["todate"] = _actualToDate;
String token = await new Utilities().getToken();
Map userHeader = {"token": token};
print(reportData.toString());
if(await new Utilities().checkInternet())
{
try
{
http.Response response = await http.post(EndPointUrl.report,body: reportData,headers: userHeader);
String message = json.decode(response.body)['message'];
List<ReportData> data = json.decode(response.body)['data'];
data.forEach((reportData){
_data.add(new PieData(reportData.menuname,reportData.itemcount));
});
notifyListeners();
}
catch(error)
{
_errorMessage = "Server error";
notifyListeners();
}
}
else
{
_progressStatuc = false;
_chartVisible = false;
_errorVisible = true;
_errorMessage = "No Internet Connection";
notifyListeners();
}
}
}
Report.dart
class Report extends StatefulWidget
{
#override
State<StatefulWidget> createState() {
return ReportState();
}
}
class ReportState extends State<Report>
{
#override
Widget build(BuildContext context) {
final reportProvider = Provider.of<ReportProvider>(context);
reportProvider.getReportData();
//yprint(reportProvider.data.toString());
if(reportProvider.errorMessage != null && reportProvider.errorMessage.contains("Internet"))
{
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("Error"),
content: Text("${reportProvider.errorMessage}"),
actions: <Widget>[
FlatButton(
child: Text("ok"),
onPressed: (){
Navigator.pop(context);
},
)
],
);
});
}
return Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(8, MediaQuery.of(context).size.height*0.05,0, 0),
child: Text(
"Report",
style: TextStyle(fontSize: 28,color: Colors.black),
),
),
Row(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
margin: EdgeInsets.fromLTRB(8, 8, 0, 0),
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "fromdate");
},
child: Text(
"${reportProvider.fromDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
),
Expanded(
flex: 1,
child: Text(
"To",
style: TextStyle(fontSize: 16,color: Colors.grey),
),
),
Expanded(
flex: 3,
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "todate");
},
child: Text(
"${reportProvider.toDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: (){},
child: Icon(
Icons.check,
color: Theme.of(context).accentColor,
),
),
)
],
),
// Visibility(
// visible: reportProvider.chartVisible,
// child: charts.PieChart<PieData>(
// ),
// ),
Expanded(
child: Visibility(
visible: reportProvider.errorVisible,
child: Container(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/undraw_report.svg',width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height*0.40),
Text(
"No Reports are available",
style: TextStyle(color: Colors.black,fontSize: 20),
)
],
),
),
),
)
],
),
],
);
}
}
As per the documentation, build() method is going to be called every time something changes. If you want calls to be triggered just once, you could use initState() method and add some helpers methods to update the UI. An example can be found here: https://flutter.dev/docs/get-started/flutter-for/android-devs#what-is-the-equivalent-of-runonuithread-in-flutter
Example for async loading from the previous link, special attention to loadData method:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
#override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
},
),
);
}
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}"),
);
}
Future<void> loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}

Categories

Resources