On the example below, since MyStatefulWidget has a state, it doesn't matter if setState is called on _MyAppState, because it will not be redrawn.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){setState(() { value+=1; });},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
If I used a StatelessWidget it'd be redrawn, but this is just an example, there are cases where I need to redraw a StatefulWidget when setState is called.
One option would be to give it a name and build it from the setState, but I need it to be draw in the place where it's draw right now.
Dartpad: https://dartpad.dev/968be8755d5deab1ca5c8c84a993eafc
You could directly use widget.title in the Text widget to update the counter on screen. Please see the code below :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
void changeVal(int val) {
setState(() {
value = val;
});
}
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('App Example')),
body: Row(children: [
MyStatefulWidget(
title: value.toString(),
groupValue: value % 10,
chnageVal: changeVal),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: const Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key, this.title, this.groupValue, this.chnageVal})
: super(key: key);
final String title;
final int groupValue;
final Function(int) chnageVal;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
//String title;
// #override
// void initState() {
// super.initState();
// if (widget.title!=null) {
// title = widget.title;
// } else {
// title = "";
// }
// }
//int value = 0;
List<int> numbers = List.generate(10, (index) => index);
#override
Widget build(BuildContext context) {
return Container(
width: 120,
child: Column(children: [
Text(widget.title),
...numbers
.map((number) => RadioListTile<int>(
title: Text('$number'),
value: number,
groupValue: widget.groupValue,
onChanged: (val) {
widget.chnageVal(val);
},
))
.toList()
]),
);
}
}
Just provide a unique key while calling MyStatefulWidget like MyStatefulWidget(key: UniqueKey(), title: value.toString()),.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children: [
MyStatefulWidget(key: UniqueKey(), title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}) : super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title != null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
To know more about key please go through this article.
I will recommend using Stream, better performance and not so hard to use for refresh partial UI.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
StreamController<int> _streamController = StreamController();
int value = 0;
#override
void dispose() {
_streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home:
StreamBuilder<int>(
stream: _streamController.stream,
initialData: value,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){_streamController.sink.add(value++);},
child: new Text("Add"),
)
]),
);},),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
Related
I have a simple flutter application. It's ok, but I'm trying to understand how onHover: (event){...} works, why "event" contains data? How can I make my own widget have function parameters like that?
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double dx = 0, dy = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
home: Scaffold(
body: MouseRegion(
onHover: (event) {
setState(() {
dx = event.localPosition.dx;
dy = event.localPosition.dy;
});
},
child: Center(
child: Text('$dx'),
),
),
),
);
}
}
To create your own onChange, or the like we can use ValueChanged.
For example, taking a look at the code for a TextButton() we see:
const TextButton({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
the onHover uses a ValueChanged.
You can implement your own valueChanged using this example:
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: Buttons(
onHover: (value) {
// Do something
print(value);
},
),
),
),
);
}
}
class Buttons extends StatelessWidget {
final ValueChanged<String> onHover;
Buttons({Key? key, required this.onHover}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
TextButton(
onPressed: () {
onHover('Pressed');
},
child: Text("Click me")),
Text('hi')
],
);
}
}
So this how we pass the data from the widget which is at the bottom of the widget tree.
It's more related to passing the value from bottom to top using callback functions.
Below is the simple example to demonstrate this data sharing.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _parentData = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"Parent State Value: " + _parentData.toString(),
),
ChildWidgetExample(
callbackFn: (data) {
setState(() {
_parentData = data;
});
},
)
],
);
}
}
class ChildWidgetExample extends StatefulWidget {
final Function(int) callbackFn;
const ChildWidgetExample({
Key? key,
required this.callbackFn,
}) : super(key: key);
#override
State<ChildWidgetExample> createState() => _ChildWidgetExampleState();
}
class _ChildWidgetExampleState extends State<ChildWidgetExample> {
int data = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
data.toString(),
),
const SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
setState(() {
data++;
});
widget.callbackFn(data);
},
child: const Text("Press"),
)
],
);
}
}
In Flutter you can declare Functions with parameters.
void Function(String foo) myFunction;
So you declare in as a variable in your widget component.
MyWidget({required this.myFunction});
Then when you have to call this component you can write :
...
child : MyWidget(myFunction: (String foo) {},),
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(),
);
},
);
}
}
Minimum example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
body: CustomScrollView(
slivers: [Example()],
),
));
}
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => ExampleState();
}
class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return SliverList(
delegate: SliverChildListDelegate(const <Widget>[
SizedBox(
height: 1400,
),
CheckboxWidget()
], addAutomaticKeepAlives: true));
}
#override
bool get wantKeepAlive => true;
}
class CheckboxWidget extends StatefulWidget {
const CheckboxWidget({Key? key}) : super(key: key);
#override
State<CheckboxWidget> createState() => _CheckboxWidgetState();
}
class _CheckboxWidgetState extends State<CheckboxWidget> {
late bool _personalData = false;
#override
Widget build(BuildContext context) {
return Checkbox(
checkColor: Colors.black,
onChanged: (bool? value) {
setState(() {
_personalData = value!;
});
},
value: _personalData,
);
}
}
If you click the checkbox, then scroll out of view and then back in to view.. The box becomes unchecked. This is because the widget rebuilds...setting the _personalData to false. I would of thought addAutomaticKeepAlives would prevent the widget rebuilding and keep the state of the checkbox. How do I prevent CheckboxWidget from rebuilding?
Firstly, I will choose state management or passing value to the CheckboxWidget. To answer this question, we need to save (keep alive) the state of CheckboxWidget. Therefore, we need to use AutomaticKeepAliveClientMixin on _CheckboxWidgetState instead of parent widget.
class CheckboxWidget extends StatefulWidget {
const CheckboxWidget({Key? key}) : super(key: key);
#override
State<CheckboxWidget> createState() => _CheckboxWidgetState();
}
class _CheckboxWidgetState extends State<CheckboxWidget>
with AutomaticKeepAliveClientMixin {
late bool _personalData = false;
#override
Widget build(BuildContext context) {
super.build(context);
return Checkbox(
checkColor: Colors.black,
onChanged: (bool? value) {
setState(() {
_personalData = value!;
});
},
value: _personalData,
);
}
#override
bool get wantKeepAlive => true;
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => ExampleState();
}
class ExampleState extends State<Example> {
#override
Widget build(BuildContext context) {
return SliverList(
delegate: SliverChildListDelegate(
const <Widget>[
SizedBox(
height: 1400,
),
CheckboxWidget()
],
),
);
}
}
I am creating an application where I detect a 4-time press power button and perform some action on it I try my self but I unable to perform it please if you have any solution tell me
I am using hardware_buttons: ^1.0.0 packages in my this application.
my code
import 'dart:async';
import 'package:hardware_buttons/hardware_buttons.dart' as HardwareButtons;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(debugShowCheckedModeBanner: false, home: MyHomeScreen());
}
}
class MyHomeScreen extends StatefulWidget {
MyHomeScreen({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomeScreenState createState() => _MyHomeScreenState();
}
class _MyHomeScreenState extends State<MyHomeScreen> {
String _latestHardwareButtonEvent;
var counter = 0;
StreamSubscription<HardwareButtons.LockButtonEvent> _lockButtonSubscription;
#override
void initState() {
super.initState();
_lockButtonSubscription =
HardwareButtons.lockButtonEvents.listen((event) {
setState(() {
_latestHardwareButtonEvent = 'LOCK_BUTTON';
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text("Hardware Interaction"),
centerTitle: true,
),
body: Container(
child: Column(
children: [],
),
));
}
}
I have a reusable text field class like so:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: MyTextField(
value: value,
onChange: (val) {
setState(() {
value = val;
});
},
),
),
),
);
}
}
typedef ChangeCallback = void Function(String value);
class MyTextField extends StatefulWidget {
final ChangeCallback onChange;
final String value;
const MyTextField({this.onChange = _myDefaultFunc, this.value = ""});
static _myDefaultFunc(String value){}
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
final controller = TextEditingController();
#override
void initState() {
controller.text = widget.value;
super.initState();
}
#override
void didUpdateWidget(covariant MyTextField oldWidget) {
controller.text = widget.value;
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value) {
widget.onChange(value);
},
);
}
}
As you can see, if the value is changed then onChange callback is called and also the value is again sent back to TextField. The problem with this is every time I update the value, TextField sets the cursor always to the start. Probably because the updated value is sent back to the TextField everytime? Not sure. Can you help me with this?
No need to update values in didUpdateWidget
//#override
// void didUpdateWidget(covariant MyTextField oldWidget) {
// controller.text = widget.value;
// super.didUpdateWidget(oldWidget);
// }
I just commented-out this function and the code works fine
below is the complete code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(children: [
SizedBox(height:50),
Text("text input: $value"),
SizedBox(height:50),
Center(
child: MyTextField(
value: value,
onChange: (val) {
setState(() {
value = val;
});
},
),
),
]),
),
);
}
}
typedef ChangeCallback = void Function(String value);
class MyTextField extends StatefulWidget {
final ChangeCallback onChange;
final String value;
const MyTextField({this.onChange = _myDefaultFunc, this.value = ""});
static _myDefaultFunc(String value) {}
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
final controller = TextEditingController();
#override
void initState() {
controller.text = widget.value;
super.initState();
}
// #override
// void didUpdateWidget(covariant MyTextField oldWidget) {
// controller.text = widget.value;
// super.didUpdateWidget(oldWidget);
// }
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value) {
widget.onChange(value);
},
);
}
}
Instead of using onChanged method to get the value of text field, pass a TextEditingController.
class MyTextField extends StatefulWidget {
final TextEditingController controller;
final String defaultValue;
const MyTextField({#required this.controller, this.defaultValue = ''});
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
#override
void initState() {
widget.controller.text = widget.defaultValue;
super.initState();
}
#override
Widget build(BuildContext context) {
return TextField(
controller: widget.controller,
);
}
}
This way, you can declare a TextEditingController in the parent widget to get the value like you would normally do with Flutter's native TextFormField.