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.
Related
I'm trying to build a parent widget that has a button, when clicked, it displays another widget with some text and a drop-down list. When the drop-down selection is changed, the text should change accordingly. I've included below a simplified code of what I'm trying to achieve which doesn't work. The state lifting up concept is something confusing for me as a newcomer to Flutter
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = "Empty";
void addWidget() {
setState(() {
widList.clear();
widList.add(MidWidget(
text: text,
setValue: selectValue,
));
});
}
void selectValue(String value) {
setState(() {
text = value;
});
}
List<Widget> widList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
ElevatedButton(onPressed: addWidget, child: const Text("Add Widget")),
Column(
children: widList,
)
]),
),
);
}
}
class MidWidget extends StatelessWidget {
const MidWidget({super.key, required this.text, required this.setValue});
final String text;
final Function setValue;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(text),
LowestWidget(
dropDownValue: "First",
setValue: setValue,
),
],
);
}
}
////////////////////
///////////////////
///
class LowestWidget extends StatelessWidget {
LowestWidget(
{super.key, required this.dropDownValue, required this.setValue});
final List<String> items = ["First", "Second"];
final String dropDownValue;
final Function setValue;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropDownValue,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? value) {
setValue(value);
},
items: items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
First of all, both MidWidget and LowestWidget need to be converted to StatefulWidget because we need state changes inside those widgets too.
Secondly, selectValue function should be in the MidWidget, not in the parent widget, because it attempts to change the state of text that has already been passed onto the MidWidget with its original value at the time of its instantiation. Any change in text via setState is not going to affect its value in MidWidget anymore.
Thirdly, I've introduced _value variable in both MidWidget and LowestWidget that takes its initial value from the respective parent widgets in initState and then gets value changes via setState that are then used to be displayed in Text widget in MidWidget and DropdownButton widget in LowestWidget.
Following is the revised code that is working as per your requirements. I've commented out the deletions so that you could relate it with the original code.
Hope it helps!
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = "Empty";
void addWidget() {
setState(() {
widList.clear();
widList.add(MidWidget(
text: text,
// setValue: selectValue,
));
});
}
// void selectValue(String value) {
// setState(() {
// text = value;
// });
// }
List<Widget> widList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
ElevatedButton(onPressed: addWidget, child: const Text("Add Widget")),
Column(
children: widList,
)
]),
),
);
}
}
class MidWidget extends StatefulWidget {
const MidWidget({super.key, required this.text, /*required this.setValue*/});
final String text;
// final Function setValue;
#override
State<MidWidget> createState() => _MidWidgetState();
}
class _MidWidgetState extends State<MidWidget> {
String? _value;
void selectValue(String value) {
setState(() => _value = value);
}
#override
void initState() {
_value = widget.text;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(_value!),
LowestWidget(
dropDownValue: "First",
setValue: selectValue,
),
],
);
}
}
////////////////////
///////////////////
///
class LowestWidget extends StatefulWidget {
LowestWidget(
{super.key, required this.dropDownValue, required this.setValue});
final String dropDownValue;
final Function setValue;
#override
State<LowestWidget> createState() => _LowestWidgetState();
}
class _LowestWidgetState extends State<LowestWidget> {
final List<String> items = ["First", "Second"];
String? _value;
#override
void initState() {
_value = widget.dropDownValue;
super.initState();
}
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: _value,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? value) {
setState(() => _value = value);
widget.setValue(value);
},
items: items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
How to change the text color which is in a different widget on switch with a flutter provider?
When switch is on change text color to red else change to green. Bu don't merge first and second widgets.
When clicked switch button change other widget's text.
`
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return const Text('Change my color',
style: TextStyle(color: Colors.green));
}
}
`
The easiest way would be to pass the color down into the constructor of MyText widget, since MyText widget is being built as a child of SwitchExample which is handling the switch state.
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText(light ? Colors.green : Colors.blue)
],
);
}
}
class MyText extends StatelessWidget {
final Color color;
const MyText(this.color, {super.key});
#override
Widget build(BuildContext context) {
return Text('Change my color',
style: TextStyle(color: color));
}
}
But, if you wanted to use provider so that MyText could be a child widget anywhere below the provider widget in the tree you could use Provider with a ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class ColorModel extends ChangeNotifier {
Color color = Colors.green;
void setColor(Color color) {
this.color = color;
notifyListeners();
}
}
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ColorModel>(
create: (context) => ColorModel(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
Provider.of<ColorModel>(context, listen: false)
.setColor(value ? Colors.green : Colors.blue);
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return Consumer<ColorModel>(builder: (context, state, _) {
return Text('Change my color', style: TextStyle(color: state.color));
});
}
}
Check out flutter's docs for more info: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple
I'm new to flutter. I'm trying to make a simple automatically updating time.
I tried with RefreshIndicator but it didn't work for me. What is the correct way to make it update per second? Is it possible to make it update with the setState in the bottomNavigationBar by making recursion function?
enter image description here
import 'dart:async';
import 'package:flutter/material.dart';
int Currentindex = 0 ;
late String time1;
var today = DateTime.now();
String time()
{
today = DateTime.now();
time1 = (today.hour.toString()+" : "+today.minute.toString()+" : "+today.second.toString());
return time1;
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner : false ,
home: Firstpage()
,);
}
}
class Firstpage extends StatefulWidget {
const Firstpage({Key? key}) : super(key: key);
#override
_FirstpageState createState() => _FirstpageState();
}
class _FirstpageState extends State<Firstpage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Currentindex == 0 ? Column(mainAxisAlignment: MainAxisAlignment.center, children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context)
{
return SecondPage();
}
)
);
});
}, child: Text("Click me"))],
) : Currentindex == 1 ? Column(mainAxisAlignment : MainAxisAlignment.center,children: [Text(time(),style: TextStyle(fontSize: 80),)], ):
SizedBox()
) ,
bottomNavigationBar: BottomNavigationBar(items: const [
BottomNavigationBarItem(label: "Icecream",icon: Icon(Icons.icecream , color: Colors.white,)),
BottomNavigationBarItem(label: "Time",icon: Icon(Icons.access_time , color: Colors.white,))],
backgroundColor: Colors.blue,
onTap: (int index){setState(() {
if(Currentindex == 1){today = DateTime.now();;}
Currentindex = index;
});},
),
),
);
}
}
class SecondPage extends StatefulWidget {
const SecondPage({Key? key}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold( backgroundColor: Colors.grey,
appBar: AppBar(title: Text("Cool"),backgroundColor: Colors.transparent,),
body: Center(
child: Column(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context){return Firstpage();
}
)
);
}
);
}, child: Text("Go back"), style: ElevatedButton.styleFrom(primary: Colors.yellow , onPrimary: Colors.orange),)],
),
),
)
);
}
}
See if it helps. Ideally this kind of widget (that updates all the time) should be in leafs and by themselves, to avoid rebuilding parts of your tree unnecessarily.
class MyWidget extends StatefulWidget {
const MyWidget();
#override
State<StatefulWidget> createState() => _MyWidget();
}
class _MyWidget extends State<MyWidget> {
String lastTime = '';
#override
initState() {
super.initState();
timeUpdate.listen((time) => setState(() => lastTime = time ));
}
String get time => DateTime.now().toString();
Stream<String> get timeUpdate async* {
while(true) {
await Future.delayed(Duration(seconds: 1));
yield time;
}
}
#override
Widget build(BuildContext context) {
return Text(
time,
style: Theme.of(context).textTheme.headline4,
);
}
}
If you're trying to make a digital clock looking thing, try this:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class ClockWidget extends StatelessWidget {
const ClockWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
return Text(
DateFormat('HH:mm:ss')
.format(DateTime.now().toLocal())
.toString(),
);
},
);
}
}
I have a test edit field. When there is no text, I want a button disabled. When there is text in the TextField, I want the button enabled.
I am using flutter_hooks to reduce boiler plate code for controllers.
In the following example, when I enter test into the text field, the button never enables, because build is not triggered? How can I trigger a build when using a text editing controller with flutter hooks?
class MyHomePage extends HookWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var ctrl = useTextEditingController();
VoidCallback? onPressed;
if (ctrl.text.isNotEmpty) {
onPressed = () => print("Pressed!");
}
return Scaffold(
body: Column(
children: [
TextField(
controller: ctrl,
),
ElevatedButton(onPressed: onPressed, child: Text("Button")),
],
)
);
}
}
You can Achieve this using useState and useTextEditingController
var istextchanged = useState<bool>(false);
ctrl.addListener(() {
if (ctrl.text.isEmpty) {
istextchanged.value = false;
} else {
istextchanged.value = true;
}
});
Yourwidget
class MyHomePages2 extends HookWidget {
const MyHomePages2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var ctrl = useTextEditingController();
var istextchanged = useState<bool>(false);
ctrl.addListener(() {
if (ctrl.text.isEmpty) {
istextchanged.value = false;
} else {
istextchanged.value = true;
}
});
VoidCallback? onPressed = () {
print("change");
};
if (ctrl.text.isNotEmpty) {
onPressed = () => print("Pressed!");
}
return Scaffold(
body: Column(
children: [
TextField(
controller: ctrl,
onChanged: (v) {},
),
ElevatedButton(
onPressed: istextchanged.value ? onPressed : null,
child: Text("Button"))
],
));
}
}
Package used flutter_hooks: ^0.18.2+1
pub.dev/flutter_hooks
SampleCode
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
int myvalue = 0;
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {
// functions().then((int value) {
// setState(() {
// myvalue = value;
// });
// future is completed you can perform your task
// });
}
Future<int> functions() async {
// do something here
return Future.value();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: MyHomePages2(),
);
}
}
class MyHomePages2 extends HookWidget {
const MyHomePages2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var ctrl = useTextEditingController();
var istextchanged = useState<bool>(false);
ctrl.addListener(() {
if (ctrl.text.isEmpty) {
istextchanged.value = false;
} else {
istextchanged.value = true;
}
});
VoidCallback? onPressed = () {
print("change");
};
if (ctrl.text.isNotEmpty) {
onPressed = () => print("Pressed!");
}
return Scaffold(
body: Column(
children: [
TextField(
controller: ctrl,
onChanged: (v) {},
),
ElevatedButton(
onPressed: istextchanged.value ? onPressed : null,
child: Text("Button"))
],
));
}
}
it is not updating my widget. If I click on the switch to turn _darkMode on the switch is always moving back (doesn't change)...
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key);
#override
_SettingsScreenState createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
#override
Widget build(BuildContext context) {
bool _darkMode = false;
return Container(
child: ListView(
children: [
ListTile(
title: const Text('Lights'),
trailing: CupertinoSwitch(
value: _darkMode,
onChanged: (bool value) {
setState(() {
_darkMode = value;
print('DarkMode: $_darkMode');
});
},
),
),
],
));
}
}
_darkMode must be a field in the widget state. Move it outside the build method:
class _SettingsScreenState extends State<SettingsScreen> {
bool _darkMode = false;
#override
Widget build(BuildContext context) {