Flutter onPressed trigger State of another widget - flutter

I need help with flutter build management. My Goal is to rebuild my side navigation bar with the SmallSideMenu() or the opposite SideMenu().
Do you have an idea how I can trigger the Build process of my LargeView widget, so that it's rebuilt with the correspondent SideMenu?
The button is defined like this:
IconButton(
onPressed: () {
checkState();
},
icon: HeroIcon(
HeroIcons.arrowNarrowRight,
size: 16.0,
),
),
The value of the sideMenuOpen variable and function is set globally;
checkState() {
if (sideMenuOpen == true) {
sideMenuOpen = false;
} else {
sideMenuOpen = true;
}
}
the SideMenu is defined here.
class LargeView extends StatefulWidget {
const LargeView({
Key? key,
}) : super(key: key);
#override
State<LargeView> createState() => _LargeViewState();
}
class _LargeViewState extends State<LargeView> {
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: SizedBox(
width: 100,
child: sideMenuOpen ? SideMenu() : SmallSideMenu(),
),
),
Expanded(
flex: 10,
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(20.0),
color: greyColor,
child: Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0)),
child: localNavigator())),
),
)
],
);
}
}

make sideMenuOpen a state in stateful widget and use setState as
checkState() {
if (sideMenuOpen == true) {
setState((){
sideMenuOpen = false;
});
} else {
setState((){
sideMenuOpen = true;
});
}
}
if you want to keep state (open/close) of navigation bar globally for that use some state management like provider instead of using global function.

You can use ValueNotifier instead of single bool. And to update UI it can be used on ValueListenableBuilder.
final ValueNotifier<bool> sideMenuOpen = ValueNotifier(false);
And
child: SizedBox(
width: 100,
child: ValueListenableBuilder<bool>(
valueListenable: sideMenuOpen,
builder: (context, value, child) => value
? SideMenu(),
: SmallSideMenu(),
),
),
And change value like
sideMenuOpen.value = true;

Related

How do I keep a instantiated variable unchanged after using Provider.of

I have a page where I can choose a user theme color that is used as colors in my app. I have it set up so a modal bottom sheet pops up with the color options and then sets it using Provider so when the navigation is popped the new color can be seen on MyView.
Problem
When the user makes a change BUT hits the close button I essentially want to revert all changes made, so to try and tackle this I have a variable called loggedInUser which I initialise in my init State function and I keep out of the build method. So its set once and that's it. The plan is that if the user hits the close button I use Provider to set the details back to the data in loggedInUser (which shoulldn't have the updated color choices).
This does not happen and loggedInUser though not reinitialised has the new colors I chose.
Code
class MyView extends StatefulWidget {
static const String id = "my_view";
#override
State<MyView> createState() => _MyViewState();
}
class _MyViewState extends State<MyView> {
UserDto loggedInUser;
#override
void initState() {
super.initState();
loggedInUser = Provider.of<UserData>(context, listen: false).user;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kThemeColor,
body: Column(
children: [
SafeArea(
child: CloseButton(
onPressed: () {
var test = loggedInUser;
//when debugged, test has the new color, not the old one it was initialised to back in initState();
//i want the old values to persist
Navigator.pop(context);
},
color: Colors.white,
),
),
Expanded(
child: Container(
height: double.infinity,
width: double.infinity,
decoration: kCurvedContainerBoxDecoration,
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(context).viewInsets.bottom),
child: AccountThemePickerView(),
),
),
);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
UserHelper.getColorFromString(
Provider.of<UserData>(context).user.themeColor),
),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
)
],
),
),
),
)
],
),
);
}
}
class AccountThemePickerView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Color(0xff757575),
child: Container(
decoration: kModalBottomSheetBoxDecoration,
padding: EdgeInsets.only(left: 15, bottom: 30, right: 15, top: 15),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
crossAxisSpacing: 30,
mainAxisSpacing: 30,
children: [
AccountThemePickerColor(
colorName: "Coral Red", color: Color(0xffff6961)),
AccountThemePickerColor(
colorName: "Forest Green", color: Color(0xff129a7d)),
],
),
),
);
}
}
class AccountThemePickerColor extends StatelessWidget {
final Color color;
final String colorName;
AccountThemePickerColor({this.colorName, this.color});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
Provider.of<UserData>(context, listen: false)
.updateUserThemeColor(colorName, color.toString());
Navigator.pop(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
backgroundColor: MaterialStateProperty.all<Color>(color),
),
);
}
}
UserData class
class UserData extends ChangeNotifier{
UserDto user;
void setUser(UserDto userDto){
user = userDto;
notifyListeners();
}
void updateUserThemeColor(String themeColorName, String themeColor){
//note I have a helper method which simply converts string to color, for your debug purposes you can just use an actual Color value
user.themeColor = themeColor;
user.themeColorName = themeColorName;
notifyListeners();
}
}
I believe it has something to do with copy constructors.
For example, this code:
class X{
int y;
int z;
X(this.y, this.z);
}
void main() {
X obj1 = X(2,3);
X obj2 = obj1;
X obj3 = obj2;
obj1.y = 10;
print(obj2.y);
print(obj3.y);
}
outputs
10
10
because variables are references to objects. And when you assign an object to another object, it points to the same location in memory instead of copying its elements.
Provider.of<UserData>(context, listen: false).user; would return the same object each time it is called. So, you change its value. And hence, the loggedInUser also changes.
Try to create a new object and store data in it.

quantity increase decrease display counter field

New to flutter and tried several solutions I cannot get working for simple item counter
Example icons on row. 0 should increase decrease as i press icon +1 or -1
Donut +1 0 -1
When I press plus_one icon button +1 it updates the print terminal so I know button is working. if I hot refresh the phone emulator the counter and display icon on phone updates to correct count.
How do I tell TextBox to update state ( show updated count on screen) when counter has been increased by OnPress in IconBoxState?
'''
class _IconBoxState extends State<StatefulWidget> {
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
color: Colors.white,
child: Center(
child: Ink(
decoration: const ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
icon: Icon(Icons.plus_one),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
_counter += 10;
print('this is counter + $_counter');
//new TextBox(); not working....
});
},
),
),
),
),
],
);
}
}
class TextBox extends StatefulWidget {
#override
_TextBoxState createState() => _TextBoxState();
}
class _TextBoxState extends State<TextBox> {
// int _counter = 6;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(),
),
);
}
}
'''
when you are setting the state with setState it only rebuilds the widget, where it is located and other widget's that the container widget contains, so in your case you have TextBox outside the IconBox, so only IconBox is rebuilt and TextBox can't detect state change so it stays same, you have many ways to solve it. 1. somehow store both of the widget inside the container widget and rebuild the state of the container widget, you can do this, 2. use provider, 3. use InheritedWidget 4. use BloC
This is a full code of version one, so you can play around, You will need to include ContainerWidget inside the page you want it to be displayed or in the Scaffold of MaterialApp:
class ContainerWidget extends StatefulWidget {
#override
_ContainerWidgetState createState() => _ContainerWidgetState();
}
class _ContainerWidgetState extends State<ContainerWidget> {
int counter = 0;
void increase() {
counter += 1;
}
void decrease() {
counter -= 1;
}
#override
void setState(fn) {
super.setState(fn);
}
stateSetter(String event) {
if (event == "increase") {
increase();
setState(() {});
} else if (event == "decrease") {
decrease();
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
TextBox(
counter: counter,
),
IconBox(
containerSetState: stateSetter,
),
],
),
);
}
}
class IconBox extends StatefulWidget {
final Function containerSetState;
IconBox({this.containerSetState});
#override
_IconBoxState createState() => _IconBoxState();
}
class _IconBoxState extends State<IconBox> {
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
color: Colors.white,
child: Center(
child: Ink(
decoration: const ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
icon: Icon(Icons.plus_one),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
widget.containerSetState("increase");
//new TextBox(); not working....
});
},
),
),
),
),
Material(
color: Colors.white,
child: Center(
child: Ink(
decoration: const ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
icon: Icon(Icons.exposure_minus_1),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
widget.containerSetState("decrease");
//new TextBox(); not working....
});
},
),
),
),
),
],
);
}
}
class TextBox extends StatefulWidget {
final int counter;
TextBox({this.counter});
#override
_TextBoxState createState() => _TextBoxState();
}

How to disable the tooltip dynamically in flutter? [duplicate]

This question already has answers here:
how to disable tooltip dynamcically in flutter?
(4 answers)
Closed 2 years ago.
I can disable the tooltip statically.
But I want to disable tooltip dynamically when i click flatbutton.But Couldnt disable dynamically and i have no idea to do that.
If I give statically false. it works fine.
For example : If add child like TopToolbar(showTooltip : false),it works fine,
But If i give toolbar.showTooltip = false in Flatbutton onPressed method,it doesnt work.
I want to disble it in dynamically. please help me to do that.
This is my code:
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(home: HelloWorld(),debugShowCheckedModeBanner: false,));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
TopToolbar toolbar = new TopToolbar();
toolbar.showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
bool showTooltip;
final Color backgroundColor;
final double height;
bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,this.showTooltip=true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: (){},
tooltip: widget.showTooltip ? "Bookmark" : null,
),
onPressed: (){},
),
),
],
),
);
} else {
return Container();
}
}
}
You have to store whether to show the tooltip in _HelloWorldState, not in the TopToolbar.
This would lead to doing something like this in _HelloWorldState:
class _HelloWorldState extends State<HelloWorld> {
bool showTip = true;
bool check = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(showTip),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
showTip = false;
});
},
),
]),
),
));
}
}
showTooltip should also be marked as final in TopToolbar class.
Your current implementation creates a new TopToolbar widget, it doesn't modify the existing widget. TopToolbar toolbar = new TopToolbar(); creates a completely different widget, just one that isn't ever mounted and shown. Therefore, toolbar.showTooltip = false; has no visible effect.
Alternatively to what I have shown you can access of the State of the TopToolbar using a GlobalKey, but I wouldn't recommend this for a beginner, it isn't necessary for your implementation at the moment, and GlobalKeys are relatively expensive.
This is too simple buddy,
make 1 global variable below main method
bool isTooltipActive = true;
Now change onPressed method like this
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
if(isToolTipAvtive == false){
isToolTipAvtive = true;
}else{
isToolTipAvtive = false;
}
});
},
),
And change bookmark tooltip line like this
tooltip: isToolTipAvtive ? "Bookmark" : null,

How can I pass a custom widget into another custom widget in flutter?

I am trying to pass a custom made container (with background color, title, and onPressed properties) into another custom widget that creates a row of three of these containers. The goal is to be able to input titles for each of these buttons in the second widget like so, TriButton(title1, title2, title3). Any tips or tricks would be appreciated!
Custom container
class RectButton extends StatelessWidget {
RectButton({this.buttonChild, this.bgColor, this.onPress});
final Widget buttonChild;
final Color bgColor;
final Function onPress;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPress,
child: Container(
constraints: BoxConstraints.expand(width: 100, height: 50),
child: Center(child: buttonChild),
decoration: BoxDecoration(
color: bgColor,
shape: BoxShape.rectangle,
border: Border.all(width: 1, color: Colors.white)),
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
),
);
}
}
`Tri-button code`
enum Weight {
ideal,
actual,
adjusted,
}
class TriButton extends StatefulWidget {
TriButton({this.title1, this.title2, this.title3, this.buttonChild});
final Text title1;
final Text title2;
final Text title3;
final RectButton buttonChild;
#override
_TriButtonState createState() => _TriButtonState();
}
class _TriButtonState extends State<TriButton> {
Weight selectedWeight;
#override
Widget build(BuildContext context) {
return Center(
child: Container(
constraints: BoxConstraints(maxWidth: 300),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: RectButton(
buttonChild: GOAL TO ENTER TITLE HERE,
onPress: () {
setState(() {
selectedWeight = Weight.adjusted;
});
},
bgColor: selectedWeight == Weight.adjusted
? Colors.orange[600]
: Colors.grey[600],
),
),
When using a StatefulWidget you need to use "widget.property" in your implementation.
In your case
Expanded(
child: RectButton(
buttonChild: Text(widget.title1),
onPress: () {
setState(() {
selectedWeight = Weight.adjusted;
});
},
bgColor: selectedWeight == Weight.adjusted
? Colors.orange[600]
: Colors.grey[600],
),
),
Expanded(
child: RectButton(
buttonChild: Text(widget.title2),
onPress: () {
setState(() {
selectedWeight = Weight.adjusted;
});
},
bgColor: selectedWeight == Weight.adjusted
? Colors.orange[600]
: Colors.grey[600],
),
),
.....

flutter want call parent view upon its child to refresh main view

I have a Stateful widget that has a child buttons and set of stateful containers (visible and invisible).
I want to try here is when i call the specific button, the specific button will refresh all layout and change it on its specifict view by setting visible while the others are not visible.
like this:
button1 = view1;
button2 = view2;
button3 = view3;
if (button1 is pressed){
view1 is visible}
else{
not visible}
upon my code, upon my first view(login button),I've set to go with my main_page like this:
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainPage(change1: true,change2: false,change3: false,)),
);
},
now i was display my main_page view (with a child view that has visibility property).
this is my code on main_page:
class MainPage extends StatefulWidget {
final bool change1 ;
final bool change2;
final bool change3 ;
const MainPage({Key key, this.change1,this.change2,this.change3}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Container(
constraints: BoxConstraints.expand(
height: 280.0,
),
decoration: BoxDecoration(
image: new DecorationImage(
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.blueAccent, BlendMode.colorBurn),
image: new ExactAssetImage("images/vector.jpg"),
),
),
child: Stack(
children: <Widget>[
Container(
alignment: Alignment.bottomCenter,
child: Row(
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
print("i pressed Official Business");
MainPage(change1: true,change2: false,change3: false);
//TODO: here is my problem, when i call the main_page on its page,
// the value of change1, change2, and chaange3 is not updating
// so that icanot update my view .
});
},
child: Container(
height: 50.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Text(
"Official Business",
style: TextStyle(
fontSize: 20.0, color: Colors.white),
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
print("i pressed file an OB");
MainPage(change1: false,change2: true,change3: false);
//TODO: here is my problem, when i call the main_page on its page,
// the value of change1, change2, and chaange3 is not updating
// so that icanot update my view .
});
},
child: Container(
height: 50.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Text(
"File an OB",
style: TextStyle(
fontSize: 20.0, color: Colors.white),
),
),
),
),
),
),
],
),
),
],
),
),
//TODO:
new Visibility(
//Called changed and viewOne
visible: widget.change1,
child: OfficialBusiness(),
),
new Visibility(
//Called not changed and viewTwo
visible: widget.change2,
child: FilingOb(),
),
new Visibility(
//Called not changed and viewTwo
visible: widget.change3,
child: ViewOfficialBusiness(),
)
],
),
),
);
}
}
the fillingob/officialbusiness/ViewOfficialBusiness includded was set of stateful layout, i didn't add the code to prevent over views.
sorry, in new on this programming language and i've like to enlighten with these problems i've encountered if my code is possible or not.Also if you need more reference just ping on comment so that i can provide my other codes
bool change1;
bool change2;
bool change3;
#override
initState() {
super.initState();
change1 = widget.change1;
change2 = widget.change2;
change3 = widget.change3;
}
void setChange1() {
setState(() {
change1 = true;
change2 = change3 = false;
});
}
// GestureDetector(onTap: setChange1)
// Visibility(
// visible: change1,
// child: OfficialBusiness(),
// )
Or use enum :
enum MyView {
officialBusiness,
filingOb,
viewOfficialBusiness,
}
MyView current;
// GestureDetector(onTap: () => setState(() { current = MyView.officialBusiness; }))
// Visibility(
// visible: current == MyView.officialBusiness,
// child: OfficialBusiness(),
// )
In Flutter, the standard way to call functions on a parent widget when something occurs in one of its children would be to pass a function from the parent widget to the child, so that the function gets triggered from the child, with data that only the parent widget knows.
In your case, in your first view, you could define a new method like this:
void onChildPressed() {
setState(() {
// Here you change the boolean change1, change2, whatever you want
});
}
And then, in the child view, you have to define a function parameter, such that your child can receive the function as a parameter, and trigger it from wherever you want in your child widget.
class MainPage extends StatefulWidget {
final bool change1 ;
final bool change2;
final bool change3 ;
final void Function() onPressed;
const MainPage({Key key, this.change1,this.change2,this.change3, this.onPressed}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
This way, you can instantiate the MainPage from your first view like this:
MainPage(change1: false,change2: true,change3: false, onPressed: onChildPressed);
And, finally, you can call the onPressed function in your MainPage, so that your child view updates the parent view the way you want.