Icon change provider index - flutter

Hi I want to change the icon when pressed button.
So I used provider method.
Icon Widget
class LockeryIcon extends StatelessWidget {
final String text;
LockeryIcon({required this.text});
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: () {
Navigator.of(context).pushNamed(SecondView);
print(text);
},
icon: Icon(context.watch<ProviderA>().isIcon),
);
}
}
Listview builder
class Abc extends StatelessWidget {
final List _lock1 = [
'abc 1',
'abc 2',
'abc 3',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: _lock1.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
LockeryIcon(text: _lock1[index]),
],
),
);
},
),
);
}
}
Main Screen
class MainView extends StatelessWidget {
const MainView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Abc(),
);
}
Second View
class SecondView extends StatelessWidget {
const SecondView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: TextButton(
onPressed: () {
context.read<LockeryProvider>().change();
Navigator.of(context).pop();
},
child: Text('Done'),
),
);
}
}
Provider
class ProviderA with ChangeNotifier {
IconData _isIcon = Icons.access_time_filled_sharp;
IconData get isIcon => _isIcon;
void change() {
_isIcon = Icons.add_location;
notifyListeners();
}
}
My problem is when I clicked this all the icons are being changed.
Is there a way to pass index to only change the relevant button???
Or my method is not correct?
Please help me on this
Thank you

You have to store an integer in your provider:
class ProviderA with ChangeNotifier {
int _index = -1;
int get isIndex => _index;
void change(int index) {
_index = index;
notifyListeners();
}
}
and check the index in your Icon class like this:
class LockeryIcon extends StatelessWidget {
final String text;
final int listViewIndex;
LockeryIcon({required this.text,required this.listViewIndex});
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: () {
Navigator.of(context).pushNamed(SecondView);
print(text);
},
icon: Icon(context.watch<ProviderA>().isIndex == listViewIndex ? firstIcon:secondIcon),
);
}
}
finally, use the change function in which you press your button.

Related

Flutter: remove an element in the List<ObjectWidget> with index is correct but invalid in the List<Widget>

I have the list of an object and in the object have the widget.
The List<Object> can add or remove the object.
When I remove the object in this list, the value in this object is correct. but the widget in this object is incorrect.
Why? The widget in the Object should belong to this object. This should be correct. How to fix this?
example code:
Try to add a field 2 times and type the name of the first field with 'A' and 'B' for the second then remove the first field('A' should be removed). the rest of the field will show 'A'.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Center(child: MyWidget())));
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Testpage();
}
}
class Testpage extends StatefulWidget {
const Testpage({Key? key}) : super(key: key);
#override
State<Testpage> createState() => _TestpageState();
}
class _TestpageState extends State<Testpage> {
List<TestWidgetGroup> fieldList = [];
#override
Widget build(BuildContext context) {
print('\nBuild\n');
List<Widget> children = [];
for (int index = 0; index < fieldList.length; index++) {
print(
'\nindex at $index, name = ${fieldList[index].object.name}, phone = ${fieldList[index].object.phone}'); // correct value
children.add(Padding(
padding: const EdgeInsets.all(10.0),
child: Column(children: [
fieldList[index].nameField,
fieldList[index].phoneField,
Center(
child: TextButton(
onPressed: () => setState(() => fieldList.removeAt(index)),
child: const Text('remove'))),
]),
));
}
return Scaffold(
backgroundColor: Colors.brown,
body: SafeArea(
child: Column(
children: children +
[
Center(
child: TextButton(
onPressed: () =>
setState(() => fieldList.add(TestWidgetGroup())),
child: const Text('add')))
],
)));
}
}
class TestWidgetGroup {
TestObject object = TestObject();
late Widget nameField =
buildTextField((v) => object.name = v, 'name: ${object.name}');
late Widget phoneField =
buildTextField((v) => object.phone = v, 'phone: ${object.phone}');
}
class TestObject {
String? name;
String? phone;
}
Widget buildTextField(Function(String?) onChanged, String text) {
print(text);
return TextFormField(onChanged: onChanged);
}
Now I can do it by using the Key. Add List<Key> fieldKeyList = []; with a unique string.
code:
import 'package:flutter/material.dart';
import 'package:mongo_dart/mongo_dart.dart' as mongo;
class Testpage extends StatefulWidget {
const Testpage({Key? key}) : super(key: key);
#override
State<Testpage> createState() => _TestpageState();
}
class _TestpageState extends State<Testpage> {
List<TestWidgetGroup> fieldList = [];
List<Key> fieldKeyList = [];
#override
Widget build(BuildContext context) {
print('\nBuild\n');
List<Widget> children = [];
for (int index = 0; index < fieldList.length; index++) {
print('\nindex at $index, name = ${fieldList[index].object.name}, phone = ${fieldList[index].object.phone}'); // correct value
children.add(Padding(
key: fieldKeyList[index],
padding: const EdgeInsets.all(10.0),
child: Column(children: [
fieldList[index].nameField,
fieldList[index].phoneField,
Center(
child: TextButton(
onPressed: () {
fieldKeyList.removeAt(index);
setState(() => fieldList.removeAt(index));
},
child: const Text('remove'))),
]),
));
}
return Scaffold(
backgroundColor: Colors.brown,
body: SafeArea(
child: Column(
children: children +
[
Center(
child: TextButton(
onPressed: () {
fieldKeyList.add(Key(mongo.ObjectId().$oid));
setState(() => fieldList.add(TestWidgetGroup()));
},
child: const Text('add')))
],
)));
}
}
class TestWidgetGroup {
TestObject object = TestObject();
late Widget nameField = buildTextField((v) => object.name = v, 'name: ${object.name}');
late Widget phoneField = buildTextField((v) => object.phone = v, 'phone: ${object.phone}');
}
class TestObject {
String? name;
String? phone;
}
Widget buildTextField(Function(String?) onChanged, String text) {
print(text);
return TextFormField(onChanged: onChanged);
}
However, why does the flutter make the controller remove always remove the last of the list instead of the target index?

Flutter - How to Extract Widget with onPressed setState inside?

I want to Extract a Widget with onPressed setState inside but I get the Message "Reference to an enclosing class method cannot be extracted."
Is there a way to do that?
I would like to divide my code into different widgets so that it remains clear. Here is simplified an example of the code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Calculator(),
);
}
}
class Calculator extends StatefulWidget {
#override
_CalculatorState createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
var myValue = 0;
void calculate() {
myValue = 12;
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: TextButton(
onPressed: () {
setState(() {
calculate();
});
},
child: Text(
'Button 001',
),
),
),
TextOutput(myValue: myValue),
],
),
);
}
}
class TextOutput extends StatelessWidget {
const TextOutput({
Key key,
#required this.myValue,
}) : super(key: key);
final int myValue;
#override
Widget build(BuildContext context) {
return Container(
child: Text(
myValue.toString(),
),
);
}
}
The part I want to extract into a separate widget:
Container(
child: TextButton(
onPressed: () {
setState(() {
calculate();
});
},
child: Text(
'Button 001',
),
),
),
Flutter offers VoidCallback and Function(x) (where x can be a different type) for callback-style events between child and parent widgets.
Simply You can pass Function onPressed; via constructor
Here is your Extracted Container widget:
class ExtractedContainer extends StatelessWidget {
final Function onPressed;
const ExtractedContainer({
Key key, #required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: () {
onPressed();
},
child: Text(
'Button 001',
),
),
);
}
}
And Here How to use it:
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ExtractedContainer(onPressed: calculate,),
TextOutput(myValue: myValue),
],
),
);
}
Your full code example
import 'package:flutter/material.dart';
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Calculator(),
);
}
}
class Calculator extends StatefulWidget {
#override
_CalculatorState createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
var myValue = 0;
void calculate() {
myValue = 12;
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ExtractedContainer(onPressed: calculate,),
TextOutput(myValue: myValue),
],
),
);
}
}
class ExtractedContainer extends StatelessWidget {
final Function onPressed;
const ExtractedContainer({
Key key, #required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: () {
onPressed();
},
child: Text(
'Button 001',
),
),
);
}
}
class TextOutput extends StatelessWidget {
const TextOutput({
Key key,
#required this.myValue,
}) : super(key: key);
final int myValue;
#override
Widget build(BuildContext context) {
return Container(
child: Text(
myValue.toString(),
),
);
}
}
Setstate is related to the widget you want to refresh its state. If you extract it to another place, then setState refers to the state of the new widget.
In your case, the setState will only change the state of the container encapsulating your widget which you are trying to extract and its children, it doesn't migrate upward.
Unless, you look for the state of the widget you want, using exact type, and then trigger the state there, but this is overkill, a lot harder, requires more code, than what you currently have.
You can use VoidCallback on extract widget to get onPressed event
class MyContainer extends StatelessWidget {
final VoidCallback onTap;
const MyContainer({
Key? key,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: onTap,
child: Text(
'Button 001',
),
),
);
}
}
And use like
MyContainer(
onTap: () {
print("tapped");
setState(() {
calculate();
});
},
),

Force rebuild of a stateful child widget in flutter

Let's suppose that I have a Main screen (stateful widget) where there is a variable count as state. In this Main screen there is a button and another stateful widget (let's call this MyListWidget. MyListWidget initialize it's own widgets in the initState depending by the value of the count variable. Obviously if you change the value of count and call SetState, nothing will happen in MyListWidget because it create the values in the initState. How can I force the rebuilding of MyListWidget?
I know that in this example we can just move what we do in the initState in the build method. But in my real problem I can't move what I do in the initState in the build method.
Here's the complete code example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
});
},
),
),
MyListWidget(count),
],
));
}
}
class MyListWidget extends StatefulWidget {
final int count;
const MyListWidget(this.count, {Key? key}) : super(key: key);
#override
_MyListWidgetState createState() => _MyListWidgetState();
}
class _MyListWidgetState extends State<MyListWidget> {
late List<int> displayList;
#override
void initState() {
super.initState();
displayList = List.generate(widget.count, (int index) => index);
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(displayList[index].toString()),
),
itemCount: displayList.length,
),
);
}
}
I don't think the accepted answer is accurate, Flutter will retain the state of MyListWidget because it is of the same type and in the same position in the widget tree as before.
Instead, force a widget rebuild by changing its key:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
});
},
),
),
MyListWidget(count, key: ValueKey(count)),
],
),
);
}
}
Using a ValueKey in this example means the state will only be recreated if count is actually different.
Alternatively, you can listen to widget changes in State.didUpdateWidget, where you can compare the current this.widget with the passed in oldWidget and update the state if necessary.
USE THIS:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
MyListWidget myListWidget = MyListWidget(5);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
myListWidget = MyListWidget(count);
});
},
),
),
myListWidget,
],
));
}
}

How can I move forward and backward data between to different stateful widgets in Flutter?

There is count variable in the first stateful widget, I have passed it to Setting class. And, Setting class passes it toSettingStateBuilder. Then, its value is changed in incrementing() in SettingStateBuilder. I want the updated value to return back to HomePageBody for further work. How can I do that?
The first stateful widget is created as follow:
class HomePage extends StatefulWidget {
#override
HomePageBody createState() => HomePageBody();
}
class HomePageBody extends State<HomePage> {
int count=0;
#override
Widget build(BuildContext context) {
...
new Setting(count);
}
}
The second stateful widget is created as follow:
class Setting extends StatefulWidget {
int count;
Setting(this.count);
#override
SettingStateBuilder createState() => SettingStateBuilder(count);
}
class SettingStateBuilder extends State<Setting> {
int count;
SettingStateBuilder(this.count);
#override
Widget build(BuildContext context) {
return new Container(
new Text(count.toString());
....
onPressed: () => setState(() => incrementing(context))),
);
}
incrementing(context) { count += 1; }
}
You could add a Function property to the Settings widget that will be called when the counter is incremented, and pass that function when you create the widget so in HomePage you can update the counter:
class HomePage extends StatefulWidget {
#override
HomePageBody createState() => HomePageBody();
}
class HomePageBody extends State<HomePage> {
int count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
MaterialButton(
child: Text('Settings'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => Settings(
count,
(newCount) {
setState(
() {
count = newCount;
},
);
},
),
),
);
},
)
],
),
body: Center(
child: Text('$count'),
),
);
}
}
class Settings extends StatefulWidget {
final int count;
final Function(int) onCounterChanged;
Settings(this.count, onCounterChanged);
#override
SettingsStateBuilder createState() => SettingsStateBuilder(count);
}
class SettingsStateBuilder extends State<Settings> {
int count;
SettingsStateBuilder(this.count);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count'),
MaterialButton(
child: Text('Increment'),
onPressed: () => setState(
() {
increment();
},
),
),
],
),
),
);
}
increment() {
count += 1;
widget.onCounterChanged(count);
}
}
If you are dealing with a more complex use case I suggest you to read for how to approach state management in Flutter, some resources:
https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro
https://flutter.dev/docs/development/data-and-backend/state-mgmt/options

Flutter switch between fragments by supporting back to previous fragment

in this link in SF, #martinseal1987 show us how can we use separated widgets link with android fragments.
I implemented this solution on my project and after running project i dont have any problem to show first widgets as an Fragment, but when i press to back button my screen goes to black and couldn't back to previous widgets as an fragment
i think that is should be this:
Problem is on navigateBack and customPop methods and i can attach fragment by pressing on button
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
title: 'AndroidMonks',
home: Scaffold(
appBar: AppBar(
title: Text('Androidmonks'),
backgroundColor: Colors.orangeAccent,
),
body: Home(),
),
));
}
class Home extends StatefulWidget {
Home({
Key key,
}) : super(key: key);
#override
State<Home> createState()=>_Home();
}
class _Home extends State<Home> {
String title = "Title";
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
navigateTo(_currentIndex);
//each fragment is just a widget which we pass the navigate function
List<Widget> _fragments =[Fragment1(),Fragment2(),Fragment3()];
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
customPop(context);
},
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child:Text('PRESS'),
onPressed: (){
_currentIndex++;
navigateTo(_currentIndex);
},
),
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
customPop(BuildContext context) {
if (_backstack.length - 1 > 0) {
navigateBack(_backstack[_backstack.length - 1]);
} else {
_backstack.removeAt(_backstack.length - 1);
Navigator.pop(context);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}
class Fragment2 extends StatefulWidget {
#override
State<Fragment2> createState() => _Fragment2();
}
class _Fragment2 extends State<Fragment2> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("_Fragment2"),
onPressed: (){
}),
);
}
}
class Fragment1 extends StatefulWidget {
#override
State<Fragment1> createState() => _Fragment1();
}
class _Fragment1 extends State<Fragment1> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment1"),
);
}
}
class Fragment3 extends StatefulWidget {
#override
State<Fragment3> createState() => _Fragment3();
}
class _Fragment3 extends State<Fragment3> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment3"),
);
}
}
I fixed some logic in your code please carefully check the changes, if you have any question don't hesitate, here is the working code
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
title: 'AndroidMonks',
home: Scaffold(
appBar: AppBar(
title: Text('Androidmonks'),
backgroundColor: Colors.orangeAccent,
),
body: Home(),
),
));
}
class Home extends StatefulWidget {
Home({
Key key,
}) : super(key: key);
#override
State<Home> createState()=>_Home();
}
class _Home extends State<Home> {
String title = "Title";
List<Widget> _fragments =[Fragment1(),Fragment2(),Fragment3()];
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
//navigateTo(_currentIndex);
//each fragment is just a widget which we pass the navigate function
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
return customPop(context);
},
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child:Text('PRESS'),
onPressed: (){
_currentIndex++;
navigateTo(_currentIndex);
},
),
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
_setTitle('$index');
}
Future<bool> customPop(BuildContext context) {
print("CustomPop is called");
print("_backstack = $_backstack");
if (_backstack.length > 1) {
_backstack.removeAt(_backstack.length - 1);
navigateBack(_backstack[_backstack.length - 1]);
return Future.value(false);
} else {
return Future.value(true);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}
class Fragment2 extends StatefulWidget {
#override
State<Fragment2> createState() => _Fragment2();
}
class _Fragment2 extends State<Fragment2> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("_Fragment2"),
onPressed: (){
}),
);
}
}
class Fragment1 extends StatefulWidget {
#override
State<Fragment1> createState() => _Fragment1();
}
class _Fragment1 extends State<Fragment1> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment1"),
);
}
}
class Fragment3 extends StatefulWidget {
#override
State<Fragment3> createState() => _Fragment3();
}
class _Fragment3 extends State<Fragment3> {
#override
Widget build(BuildContext context) {
return Center(
child: Text("_Fragment3"),
);
}
}
You can achieve this type of navigation using LocalHistoryRoute.of(context).addLocalHistoryEntry and Navigator.pop().