I want to send data from widget to another widget, in my example I want to send onPressed value as a variable.
appBar: CancelAppBar(onPressed: ),
What should I write after onPressed (in the above code) to send the value:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UnderBuild()));
to a widget in a seperate dart file?
Following is the other file:
class CancelAppBar extends StatefulWidget implements PreferredSizeWidget {
CancelAppBar({Key? key, required this.onPressed}) : super(key: key);
final ValueGetter<String> onPressed;
static final _appBar = AppBar();
#override
Size get preferredSize => _appBar.preferredSize;
#override
_CancelAppBarState createState() => _CancelAppBarState();
}
class _CancelAppBarState extends State<CancelAppBar> {
get onPressed => null;
#override
Widget build(BuildContext context) {
return AppBar(
titleSpacing: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 8.w)),
IconButton(
onPressed: ,
icon: Icon(Icons.close),
)
],
),
backgroundColor: AppColors.dark,
);
}
}
You can access any StatefulWidget variable in the State class with widget getter, like:
Also, you can notice that:
Your onPressed variable has ValueGetter<String> type which is only a typedef to String Function()
The onPressed of IconButton widget has the type void Function()
Then you can use your variable like that:
class CancelAppBar extends StatefulWidget implements PreferredSizeWidget {
CancelAppBar({Key? key, required this.onPressed}) : super(key: key);
final ValueGetter<String> onPressed;
static final _appBar = AppBar();
#override
Size get preferredSize => _appBar.preferredSize;
#override
_CancelAppBarState createState() => _CancelAppBarState();
}
class _CancelAppBarState extends State<CancelAppBar> {
#override
Widget build(BuildContext context) {
return AppBar(
titleSpacing: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 8.w)),
IconButton(
onPressed: () {
/// Access your variable by [widget.onPressed]
widget.onPressed(); /// Call it inside this function because your variable doesn't match directly the [onPressed] of [IconButton] widget
},
icon: Icon(Icons.close),
)
],
),
backgroundColor: AppColors.dark,
);
}
}
Related
I am opening a new activity with the startTimer method How do I set the cyclesVal variable to an attribute of my custom NumberSelector widget so I can pass it to the next activity. Any way to access an internal variable of the NumberSelector widget would work too since I have set the attribute to the variable.
class ExcersizeInput extends StatelessWidget {
const ExcersizeInput({super.key});
void startTimer(BuildContext context) {
int cyclesVal = 1;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimerPage(
cycleCount: cyclesVal,
),
));
}
#override
Widget build(BuildContext context) {
return Column(children: [
Column(
children: [
NumberSelector(
title: "Cycles",
),
],
),
ElevatedButton.icon(
onPressed: (() => {startTimer(context)}),
label: const Text("Start"),
icon: const Icon(Icons.start_outlined),
)
]);
}
}
Here's the NumberSelector code:
class NumberSelector extends StatefulWidget {
final String title;
const NumberSelector(
{super.key, required this.title});
#override
State<NumberSelector> createState() => _NumberSelectorState();
}
class _NumberSelectorState extends State<NumberSelector> {
int selectorValue = 1;
void updateValue(ifAdd) {
setState(() {
if (ifAdd) {
if (selectorValue < 9999) {
selectorValue++;
}
} else {
if (selectorValue > 1) {
selectorValue--;
}
}
});
}
#override
Widget build(BuildContext context) {
final ColorScheme colors = Theme.of(context).colorScheme;
return Column(
children: [
Text(widget.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Row(
children: [
GestureDetector(
child: IconButton(
onPressed: () => updateValue(false),
icon: const Icon(Icons.remove),
style: styleContainedButton),
},
),
Text(selectorValue.toString()),
style: const TextStyle(fontSize: 16)),
GestureDetector(
child: IconButton(
onPressed: () => updateValue(true),
icon: const Icon(Icons.add),
style: styleContainedButton),
},
),
],
),
],
);
}
}
To get a variable from a stateful widget in flutter it needs a key that is linked to the state of the widget. Via the key, the variables can be accessed.
Define a widget you want to access from its parent and that contains any value:
class TestWidget extends StatefulWidget {
TestWidget({Key? key}) : super(key: key);
#override
State<TestWidget> createState() => TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
int? anyvalue;
#override
Widget build(BuildContext context) {
return Container();
}
}
Declare the key in the parent widget. Make sure the name of the state does not start with an underscore:
GlobalKey<TestWidgetState> _widget_key = GlobalKey();
Give the key to the widget in the build method of the parent widget:
TestWidget(
key: _widget_key,
)
Now the value of the child widget can be accessed in the parent widget via the key:
void afunction() {
print(_widget_key.currentState!.anyvalue);
}
Question: I want to call openDrawer from onPressed() of Header.dart
The Drawer.dart, Header.dart and Home.dart files are separate.
I have triedScaffold.of(context).openDrawer();
not worked.
I tried using cotroller but it not work.
Perhaps it was not used in the right way.
I would appreciate any advice you could give me.
code:
Codes are omitted.
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: Header('App'),
endDrawer: Menu(),
)}}
class Header extends StatefulWidget implements PreferredSizeWidget {
const Header(this.heading, {Key? key});
final String heading;
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
#override
State<Header> createState() => _HeaderState(heading);
}
class _HeaderState extends State<Header> {
_HeaderState(this.heading);
final String heading;
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
// _key.currentState!.openDrawer();
},
),]
);
}
}
class Menu extends StatelessWidget {
const Menu({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer()
}
You're using an end drawer, so instead of Scaffold.of(context).openDrawer(), you should use Scaffold.of(context).openEndDrawer():
class _HeaderState extends State<Header> {
_HeaderState(this.heading);
final String heading;
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
),]
);
}
}
Side Note: You don't need to pass arguments from the StatefulWidget to the State - Every instance of State has a widget property, which you can use to access the properties of the parent widget.
In your case it would look like:
class Header extends StatefulWidget implements PreferredSizeWidget {
const Header(this.heading, {Key? key});
final String heading;
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
#override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
// Assuming this is what the heading argument is for
title: Text(widget.heading),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
),
],
);
}
}
How to change a variable of a Widget from another widget?
This is the main stateful widget called HomePage:
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text(counter.toString()), CardWidget()],
),
));
}
}
This is CardWidget which is added to HomePage:
class CardWidget extends StatefulWidget {
const CardWidget({Key? key}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
},
child: const Text('Increment'),
),
],
));
}
}
This is what is shown on the screen:
Is it possible to create a connection between the two widgets: if I tap the button something happens in the HomePage Widget? (similar to delegate in UIKit)
You can pass Function parameter.
In your CardWidget add Function parameter.
class CardWidget extends StatefulWidget {
//Add clicked function
final Function onClicked;
const CardWidget({Key? key, required this.onClicked}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
//Execute `onClicked` and pass parameter you want
_count++;
widget.onClicked(_count);
},
child: const Text('Increment'),
),
],
));
}
}
then on HomePage add onClicked parameter
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Fontanelle")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(counter.toString()),
CardWidget(
//Add onClicked
onClicked:(count){
print("Clicked "+count.toString());
}
)
],
),
));
}
}
You have some solution for this case:
1, Create GlobalKey for StatefullWidget, and you can access to State from HomePage
2, Create a Stream from Homepage and pass to StatefullWidget
3, Pass param to StatefullWidget and use didUpdateWidget on state to listen.
I remember that was my first question when did my first step in flutter, how say #dangngocduc is true but my advice is that read about BLOC
https://pub.dev/packages/flutter_bloc
This is very helpful to do this.
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();
});
},
),
I have an IndexedStack in a Scaffold that I use to manage my registration. The Registration widget itself is Stateful, but the widgets that compose it are Stateless. The parent widget looks like this:
class Registration extends StatefulWidget {
#override
_RegistrationState createState() => _RegistrationState();
}
class _RegistrationState extends State<Registration> {
int _index = 0;
void _nextPage() {
setState(() {
_index++;
});
}
void _prevPage() {
setState(() {
_index--;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
leading: new IconButton(
icon: new Icon(Icons.arrow_back,
color: Theme.of(context).primaryColor),
onPressed: () {
if (_index == 0) {
Navigator.pop(context);
} else {
_prevPage();
}
}),
elevation: 0.0,
),
body: IndexedStack(
children: <Widget>[
RegistrationPhone(_nextPage),
RegistrationName(_nextPage),
RegistrationBirthday(_nextPage),],
index: _index,
),
);
}
}
What is the best way to take data from these child widgets?
Should I pass in a callback function and hold the data in the parent? Should I pass the information down the line from widget to widget until it's submitted? I don't know what the practices are for sharing data across multiple screens.
Use Provider
Add Dependency :
dependencies:
provider: ^4.3.3
here is the Example :
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const MyApp(),
),
);
}
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
/// Makes `Counter` readable inside the devtools by listing all of its properties
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('You have pushed the button this many times:'),
/// Extracted as a separate widget for performance optimization.
/// As a separate widget, it will rebuild independently from [MyHomePage].
///
/// This is totally optional (and rarely needed).
/// Similarly, we could also use [Consumer] or [Selector].
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Count extends StatelessWidget {
const Count({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(
/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headline4);
}
}