I have a custom written stateful widget that wrapped with InkWell and I want to change the widgets variable when onTap function gets activated. Is there any way to achieve that?
Here is my custom written widget
import 'package:flutter/material.dart';
class DrawerListTile extends StatefulWidget {
final tileIcon;
final tileText;
bool isSelected = false;
DrawerListTile({this.tileIcon, this.tileText});
#override
State<DrawerListTile> createState() => _DrawerListTileState();
}
class _DrawerListTileState extends State<DrawerListTile> {
#override
Widget build(BuildContext context) {
return ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
selected: widget.isSelected,
selectedTileColor: Colors.black12,
selectedColor: Colors.black54,
leading: Icon(widget.tileIcon),
title: Text(widget.tileText),
);
}
}
And here is my InkWell widget
InkWell(
onTap: () => setState(() {
//Here is the part that I want to change the DrawerListTile's isSelected value
}),
child: DrawerListTile(
tileText: "Some Text", tileIcon: Icons.credit_card_rounded),
),
I know that I can write the onTap function inside the DrawerListTile but it is not useful in my situation so is there any way to achieve what I want?
You can do something like the below solution ... you can use your isSelected variable for this purpose.
The parent view:
class MainView extends StatefulWidget {
const MainView({Key? key}) : super(key: key);
#override
State<MainView> createState() => _MainViewState();
}
class _MainViewState extends State<MainView> {
String text = DateTime.now().toString();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('App'),
),
body: InkWell(
child: Center(child: TargetWidget(text: text)),
onTap: () {
setState(() {
text = DateTime.now().toString();
});
},
),
);
}
}
The child view:
class TargetWidget extends StatefulWidget {
String text;
TargetWidget({Key? key, required this.text}) : super(key: key);
#override
State<TargetWidget> createState() => _TargetWidgetState();
}
class _TargetWidgetState extends State<TargetWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.text),
);
}
}
You should pass your variable as a parameter to your DrawerListTile(),You can create a model class that will hold all the variables you need and pass them to the widget. Thus, whenever you call the setState function, new parameters are sent to the widget and the widget is updated.
Ex:
InkWell(
onTap: () => setState(() {
//Here is the part that I want to change the DrawerListTile's isSelected value
yourFirstVariable = something;
yourSecondVariable = something;
}),
child: DrawerListTile(
tileText: "Some Text",
tileIcon: Icons.credit_card_rounded,
drawerVariables: DrawerModel(
demoVar1 = yourFirstVariable,
demoVar2 = yourSecondVariable...
),
),
),
class DrawerModel {
final var demoVar1;
final var demoVar2;
DrawerModel ({required this.demoVar1, required this.demoVar1,});
}
class DrawerListTile extends StatefulWidget {
final tileIcon;
final tileText;
final DrawerModel drawerVariables;
bool isSelected = false;
DrawerListTile({this.tileIcon, this.tileText, this.drawerVariables});
#override
State<DrawerListTile> createState() => _DrawerListTileState();
}
class _DrawerListTileState extends State<DrawerListTile> {
#override
Widget build(BuildContext context) {
return ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
selected: widget.isSelected,
selectedTileColor: Colors.black12,
selectedColor: Colors.black54,
leading: Icon(widget.tileIcon),
title: Text(widget.tileText),
);
}
}
Related
in Flutter 3.3.4 , I want control the state of the button by passing an object with its properties
. I tried some solutions in stackoverflow (e.g How do I disable a Button in Flutter? ),but failed。
I print the flag of the object , it looks right.
here is my code
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
SwitchWidget wifiSwitch = SwitchWidget();
// SwitchWidget timeSwitch = SwitchWidget();
// SwitchWidget locationSwitch = SwitchWidget();
return MaterialApp(
title: 'Startup N1ame Generator',
home: Scaffold(
appBar: AppBar(
title: const Text('Startup Name Generator'),
),
body: Center(
child: Row(
children: [
Column(children: [wifiSwitch]),
Column(children: [ButtonWidget(wifiSwitch)])
],
),
),
),
);
}
}
class SwitchWidget extends StatefulWidget {
bool flag = true;
SwitchWidget({Key? key}) : super(key: key);
#override
State<SwitchWidget> createState() => _SwitchWidgetState(this);
}
class _SwitchWidgetState extends State<SwitchWidget> {
SwitchWidget switchWidget;
_SwitchWidgetState(this.switchWidget);
#override
Widget build(BuildContext context) {
return Container(
child: Switch(
value: switchWidget.flag,
onChanged: (newValue) => {
setState(() {
switchWidget.flag = newValue;
print("-----------${switchWidget.flag}");
})
},
),
);
}
}
class ButtonWidget extends StatefulWidget {
late SwitchWidget _switchWidget;
SwitchWidget get switchWidget => _switchWidget;
set switchWidget(SwitchWidget switchWidget) => {
print('The ButtonWidget is $switchWidget.'),
_switchWidget = switchWidget
};
ButtonWidget(switchWidget, {Key? key}) : super(key: key) {
this.switchWidget = switchWidget;
}
#override
State<ButtonWidget> createState() => _ButtonWidgetState(switchWidget);
}
class _ButtonWidgetState extends State<ButtonWidget> {
SwitchWidget switchWidget;
_ButtonWidgetState(this.switchWidget);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(50, 1, 1, 1),
child: ElevatedButton(
// color: Colors.blue,
// disabledColor: Colors.grey,
// textColor: Colors.black,
child: Text("123"),
// onPressed: () {},
onPressed: this.switchWidget.flag ? _incrementCounter : null,
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith(
(states) {
if (states.contains(MaterialState.disabled)) {
return Colors.grey;
} else {
return Colors.white;
}
},
),
)),
);
{}
}
void _incrementCounter() {
print("object******** ${this.switchWidget.flag}");
}
}
Why do you pass a reference of SwitchWidget to _SwitchWidgetState? You should move the property bool flag = true; to _SwitchWidgetState and then change it directly in setState(() => flag = newValue);.
Also, your ButtonWidget is not rebuilt on change in SwitchWidget. You'll have to use some sort of state management in order to disable the button on a state change of your switch widget.
For example using callbacks:
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
#override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _isDisabled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
SwitchWidget(initialValue: true, onChanged: (val) => setState(() => _isDisabled = val)),
ButtonWidget(isDisabled: _isDisabled),
],
);
}
}
class SwitchWidget extends StatefulWidget {
final bool initialValue;
final void Function(bool) onChanged;
const SwitchWidget({super.key, required this.onChanged, required this.initialValue});
#override
State<SwitchWidget> createState() => _SwitchWidgetState();
}
class _SwitchWidgetState extends State<SwitchWidget> {
late bool _value;
#override
void initState() {
super.initState();
_value = widget.initialValue;
}
#override
Widget build(BuildContext context) {
return Switch(
value: _value,
onChanged: (val) {
setState(() => _value = val);
widget.onChanged(val);
},
);
}
}
class ButtonWidget extends StatelessWidget {
final bool isDisabled;
const ButtonWidget({super.key, required this.isDisabled});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isDisabled
? null
: () {
//Some logic
},
child: Text("Press me!"),
);
}
}
You can pass null where you place your function, or even some of Flutter's Widgets already have the enabled property. But setState and change the function to null and you should get what you want.
I want to create a ModalBottomSheet, which opens a lot of RaisedButtons. These buttons should change there Color after pressing.
Therefore I created a StatefulWidget named "Buttons".
In my MainClass i use
for (String item in myList) Buttons(item)
to commit the Strings of myList to Buttons.
But i have 2 Problems in Buttons:
String item should be final, but then i can't use it as a setter
(I want to get rid of the blue underlining of Buttons, which tell me: This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final: )
The other problem is, that in my buildMethod "item" isn't defined.
Why can't I use String item in my build Method?
class Buttons extends StatefulWidget {
String item;
Buttons(String item) {
this.item = item;
}
#override
_ButtonsState createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> {
bool pressAttention = false;
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: RaisedButton(
child: Text(item),
color: pressAttention ? Colors.grey : Colors.blue,
onPressed: () => setState(() => pressAttention = !pressAttention),
),
),
);
}
}
Replace Text(item) by Text(widget.item)
class Buttons extends StatefulWidget {
final String item;
Buttons(this.item);
#override
_ButtonsState createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> {
bool pressAttention = false;
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: RaisedButton(
child: Text(widget.item),
color: pressAttention ? Colors.grey : Colors.blue,
onPressed: () => setState(() => pressAttention = !pressAttention),
),
),
);
}
}
There is a regular "Text" and it changes to "TextField" when I click the button I would like to know how to do this
IconButton(
icon: Icon(
Icons.edit,
color: Color(0xFF8D8D8D),
),
onPressed: null),
try this
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool isTextFild = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
isTextFild ? TextField() : Text('some text'),
FlatButton(
child: Text('Show Text Field'),
onPressed: () {
setState(() {
isTextFild = true;
});
},
)
],
),
),
);
}
}
This should be enough, a TextFormField that toggles between readOnly and not. This way, you'll ensure that your view doesn't "jump" by switching widgets and you'll be always using the same object, only toggling properties.
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _isEditing = false;
void _edit() {
setState(() => _isEditing = true);
}
#override
Widget build(BuildContext context) {
return TextFormField(
readOnly: _isEditing,
);
}
}
You can do something like this:
class Sample extends StatefulWidget {
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
bool _editMode = false;
#override
Widget build(BuildContext context) {
return Row(children: <Widget>[
_editMode ? TextField(...) : Text(...),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_editMode = !_editMode;
});
})
]);
}
}
Note: If you want to set an initial value for textfield you should use textformfield instead.
The thing I'm trying to do is, to change the colour of a RawMaterialButton when the button is clicked. Read about StatefulWidget and it seemed like it should work, but for some reason it doesn't.
flutter: Another exception was thrown: setState() called in constructor: ButtonTest#1a93b(lifecycle state: created, no widget, not mounted)
ButtonTest class:
class ButtonState extends StatefulWidget {
#override
State createState() => ButtonTest();
}
class ButtonTest extends State<ButtonState> implements Cipher {
#override
String icon = '';
#override
String title = '';
bool enabled = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: RawMaterialButton(
shape: CircleBorder(side: BorderSide(color: Colors.black)),
fillColor: enabled ? Colors.blue : Colors.red,
onPressed: () {
setState(() {
this.enabled = true;
});
},
padding: EdgeInsets.all(0)),
);
}
}
Cipher class:
abstract class Cipher {
String icon;
String title;
Widget build(BuildContext context);
}
getCiphers()
getCiphers() {
final List<Cipher> ciphers = new List();
ciphers.add(ButtonTest());
return ciphers;
}
Main class:
void main() => runApp(CipherTools());
class CipherTools extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'CipherTools',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CipherScreen(
ciphers: getCiphers(),
),
);
}
}
class CipherScreen extends StatelessWidget {
final List<Cipher> ciphers;
CipherScreen({Key key, #required this.ciphers}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ciphers'),
),
body: ListView.builder(
itemCount: ciphers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(ciphers[index].title),
// When a user taps on the ListTile, navigate to the DetailScreen.
// Notice that we're not only creating a DetailScreen, we're
// also passing the current todo through to it!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(cipher: ciphers[index]),
),
);
},
);
},
),
);
}
}
class DetailScreen extends StatelessWidget {
// Declare a field that holds the Todo
final Cipher cipher;
// In the constructor, require a Todo
DetailScreen({Key key, #required this.cipher}) : super(key: key);
#override
Widget build(BuildContext context) {
return cipher.build(context);
}
}
What am I doing wrong here?
Wrap setState() like this.
if(this.mounted) {
setState(() {
this.enabled = true;
});
}
A couple of things:
ButtonState should be called ButtonTest because this is the
StatefulWidget
ButtonTest should be called ButtonTestState because this is the State.
Then in DetailScreen, in the build() method, you could return the StatefulWidget (ButtonTest), like this:
#override
Widget build(BuildContext context) {
return ButtonTest();
}
So I have a scaffold with body is a list view. And I have an appbar that manage its stage. Here my appbar code :
import 'package:flutter/material.dart';
class HgAppBar extends StatefulWidget implements PreferredSizeWidget {
final String title;
final List<Widget> actions;
HgAppBar({this.title, this.actions, Key key}) : super(key: key);
#override
HgAppBarState createState() => HgAppBarState();
#override
Size get preferredSize => new Size.fromHeight(kToolbarHeight);
}
class HgAppBarState extends State<HgAppBar> {
bool _searchOpenned = false;
void openSeach() {
setState(() {
_searchOpenned = true;
});
}
void closeSearch() {
setState(() {
_searchOpenned = true;
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return AppBar(
title: _searchOpenned
? TextField(
decoration: InputDecoration(
filled: true,
border: null,
fillColor: Colors.white,
),
autofocus: true,
)
: Text(widget.title ?? 'No title'),
actions: _searchOpenned
? [
IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() {
_searchOpenned = false;
});
},
)
]
: widget.actions,
);
}
}
And here my page code:
class PageSales extends StatefulWidget {
final Store<AppState> store;
final String title;
final bool usePop;
PageSales(this.store, {this.title, this.usePop = false});
#override
State<StatefulWidget> createState() => _PageSales();
}
class _PageSales extends State<PageSales> {
final appBarKey = GlobalKey<HgAppBarState>();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HgAppBar(
key: appBarKey,
title: Localizations.of(context, AppLoc).text('sales_plus'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
appBarKey.currentState.openSeach();
},
)
],
),
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: FireStoreListView(
snapshot: HgFirestore.instance.productCollection.snapshots(),
itemBuilder: (context, doc) {
return WidgetProductItem(
widget.store, ProductModel.fromDocument(doc));
},
),
),
]),
),
);
}
}
so the problem is when I call the openSearch, my entire scaffold get refresh (I know it because my ListView is flashing). How do I can update my appbar without refreshing entire scaffold?
I tried your code and it seems to be fine. The screen doesn't rebuild, I'm using Flutter 2.2. I suggest adding debugPrint to make sure that the screen does get rebuild, ListView flashing isn't a definite indicator that the entire screen gets rebuild.