How to implement Gesture detection - On Tap method in child class? - flutter

I want to implement the GestureDetector method onTapin child class. Is there a way to do it in Flutter ?
ParentClass.dart
Class ParentClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: ChildClass(),
}
}
ChildClass.dart
Class ChildClass extends StatefulWidget {
methodA() // need to access methodA which is being passed to gesture detector
// How do I access methodA of parent class method here
// so whenever GestureDetector's onTap method is called, i want to handle that in ChildClass is there a way to do it ?
}

You can access the Child State methods using a unique key. Here is a minimal example:
Inside the ParentWidget, we define _childKey, a GlobalKey<_ChildWidgetState> that we then can use to access the State's method updateValue as follows:
_childKey.currentState.updateValue('Goodbye, Thierry!'),
Full source code
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: ParentWidget()),
);
}
}
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
final _childKey = GlobalKey<_ChildWidgetState>();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _childKey.currentState.updateValue('Goodbye, Thierry!'),
child: ChildWidget(key: _childKey),
);
}
}
class ChildWidget extends StatefulWidget {
const ChildWidget({Key key}) : super(key: key);
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
String value = 'Hello, Thierry!';
void updateValue(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Text(value);
}
}

In your ChildClass, return a GestureDetector. Set the child property to the rest of your widgets, and then set the onTap to call methodA. That should look something like this:
class ChildClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: SomeWidget(),
}
}

You are asking how to detect child class onTap and pass it to Parent right?
class YourChild extends StatelessWidget {
final Function parentCallback;
const YourChild({this.parentCallback});
#override
Widget build(BuildContext context) {
return GestureDetector(
// 1st option
onTap: () {
print("do something");
parentCallback();
},
)
}
}
Then for using It.
class YourParent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return YourChild( parentCallback(){
//do your stuff}
)
}
}

Related

Update variable outside a widget in Flutter?

Is it possible to update a variable outside a widget while calling it ?
Here's an example :
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(children: [
Text(example),
Widget2(example: example)
],);
}
}
class Widget2 extends StatefulWidget {
final String example;
Widget2({required this.example});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() {
widget.example = 'B'
}),
child: Text('update !'),
);
}
}
The idea here is that I want to update example using a button outside the widget.
This code is not working : example = 'A' no matter if I click the button or not, but I don't understand why since I'm calling the same variable.
Is there a simple solution to achieve this ? (by simple, I mean without the need of Provider or else.)
You can use callback method. Parent widget needed to updated, so setState is needed to be trigger on Widget1.
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(example),
Widget2(
example: example,
callback: (p0) {
setState(() {
example = p0;
});
},
),
],
);
}
}
class Widget2 extends StatefulWidget {
final String example;
final Function(String) callback;
Widget2({
required this.example,
required this.callback,
});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
widget.callback("new data");
},
child: Text('update !'),
);
}
}
You can use Notifiers, here is an example:
import 'package:flutter/material.dart';
class ExampleNotifier with ChangeNotifier {
String example = 'A';
ExampleNotifier();
setText(string x) {
example = x;
notifyListeners();
}
}
and then use it like:
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context);
return Column(
children: [
Text(exampleNotifier.example),
Widget2(),
],
);
}
}
class Widget2 extends StatefulWidget {
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context, listen: false);
return ElevatedButton(
onPressed: () {
exampleNotifier.setText('B');
},
child: Text('update !'),
);
}
}
If you want to use setState, you can use this
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
void changeExample() {
setState(() => example = "B");
}
#override
Widget build(BuildContext context) {
return Column(
children: [Text(example), Widget2(changeExample: changeExample)],
);
}
}
class Widget2 extends StatelessWidget {
final void Function() changeExample;
Widget2({required this.changeExample});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: changeExample,
child: Text('update !'),
);
}
}

how to pass a variable as a parameter to a widget two, modify it there, and return the modified value to widget one, Flutter

how to pass a variable as a parameter to a widget two, modify it there, and return the modified value to widget one.
I need to change the value of the variable when I click the "Change it" button, and that change is reflected in widget one.
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class FirstWidgetState extends State<FirstWidget> {
String c = 'start';
#override
Widget build(BuildContext context){
return Container(
child: SecondWidget(variable: c),
);
}
}
class SecondWidget extends StatefulWidget {
String variable;
SecondWidget({ this.variable });
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context){
return Container(
child: RaisedButton(child:Text('Change it'), onPressed: () {widget.variable = 'end';}),
);
}
}
It is possible to implement it easily with a callback, meaning you pass a function to your second widget, and when the button is clicked you call the function, this way you can modify whatever you want in the first widget.
Like this:
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class FirstWidgetState extends State<FirstWidget> {
String c = 'start';
#override
Widget build(BuildContext context){
return Container(
child: SecondWidget(variable: c, onChange: (newVal) {
setState(() {c = newVal;});
}),
);
}
}
class SecondWidget extends StatefulWidget {
String variable;
final onChange;
SecondWidget({ this.variable, this.onChange });
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context){
return Container(
child: RaisedButton(child:Text('Change it'), onPressed: () {widget.onChange('end');}),
);
}
}

How to set data in an inherited widget from a another widget?

So inherited widget is useful for passing data down the tree, but how do I set that data in the first place if inherited widgets are immutable?
I'm trying to set a phone number for OTP auth and then display that number on another screen. Provider is kind of advanced for me at the moment, how do I approach this?
thank you
You have to rebuild somewhere your InheritedWidget.
You can use any stage management for it, for example you can use StatefulWidget:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MyInheritedWidget extends InheritedWidget {
final int counter;
MyInheritedWidget({Key key, this.counter, Widget child})
: super(key: key, child: child);
#override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.counter != counter;
}
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MyInheritedWidget(counter: _counter, child: CounterWidget()),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_counter++;
});
},
),
);
}
}
class CounterWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("${MyInheritedWidget.of(context).counter}",
style: TextStyle(fontSize: 100));
}
}
Firstly you would use a StreamProvider for your stream of data (The same as you would using a StreamBuilder):
class Widget1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: Wrapper(),
);
}
}
Next widget has no required data
class Widget2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Widget3(),
);
}
}
Access your data via Provider.of
class Widget3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user == null) {
return Login();
} else {
return Dashboard();
}
}
}
With this method, you still need to access the data somewhere down the widget tree. You can't go up, if you want to have the ability to have a widget up the tree listen to something that happens down the tree, you will want to look at ChangeNotifier

Why does my custom flutter scaffold prevent the state from being refreshed?

Below is the minimal version of what i'm attempting to do with my app that maintains the issue. The slider still responds to being interacted with by showing it's animation, however it does not change the value or actually move. This only seems to occur because the Scaffold has been separated out to it's own widget.
I know that changing it to a stateless widget would fix it in this case, however the version in my app requires state changes.
I've tried adding UniqueKeys up and down the widget tree with no luck, though i'll admit I don't fully understand their use in this case.
Any advice, or a point in the right direction? Should I keep playing around with keys, or am I going about this in the wrong way?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scaffold Test',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _value = 20.0;
#override
Widget build(BuildContext context) {
return MyScaffold(
child: Center(
child: Slider(
min: 0,
max: 100,
value: _value,
onChanged: (double value) {
setState(() {
_value = value;
});
},
),
),
);
}
}
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => new MyScaffoldState(child);
}
class MyScaffoldState extends State<MyScaffold> {
Widget child;
MyScaffoldState(this.child);
#override
Widget build(BuildContext context) {
return Scaffold(
body: child,
);
}
}
As commented by user #pskink, removing the state constructor fixes the issue.
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => MyScaffoldState();
}
class MyScaffoldState extends State<MyScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
);
}
}
In flutter, you should never have constructors for states.
if you need to initialize state properties, use initState.

How to call a method in another stateful widget

I am building a podcasting type app, so need to call the record, stop, and play functions in many places, I created the methods, but difficulty to call these methods in other places.
main.dart
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
void startRecord() //Need to call all of these method in coming stateful widgets
void stopRecord() //
void pauseRecord()//
void resumeRecord()//
void play() //
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Scaffold(
drawer: Drawer(
elevation: 2.0,
child: ListView(
children: <Widget>[
ListTile(
title: Text('Home'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return MyApp();
},
),
);
},
),
//more code is here
Expanded(
child: GestureDetector(
child: IconButton(
icon: Icon(Icons.mic),
color: Colors.white,
iconSize: 40,
onPressed: () async {
startRecord();
}),
),
),
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
onPressed: () {
startRecord()
// need to call the method here.
}
Pressed: () {
stopRecord()
// need to call the method here.
}
Pressed: () {
play()
// need to call the method here.
}
),
}
Need to call all the methods from a first stateful widget for bottom stateful widgets
also, need to call these methods for other classes when code progress
both stateful widgets are in the main.dart. I could not call the method from the first class for the second stateful widget
This is not a rocket science, just a simple line of code, and you are done.
What you have to do, is to just call the MyHomePage() and let it accept the startRecording() to be used inside the Widget
1. Passing the data from MyApp() to MyHomePage()
#override
Widget build(BuildContext context) {
return MaterialApp(
// here you pass the your function
home: MyHomePage(onPressed: startRecording)
);
}
2. Receiving the data in MyHomePage()
class MyHomePage extends StatefulWidget {
// let it accept a function type onPressed argument
final Function onPressed;
// constructor
MyHomePage({Key key, this.onPressed}): super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
// simply call the onPressed which received your startRecording() from MyApp
onPressed: () => widget.onPressed()
}
You can get the state of a parent widget using the BuildContext of the child widget like so:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
static _MyAppState of(BuildContext context) {
return context.findAncestorStateOfType<_MyAppState>();
}
}
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
void startRecord() {
print('Hello');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
MyApp.of(context).startRecord();
return Scaffold(
body: Placeholder(),
);
}
}
Simply define that function outside the class as a stand-alone function like this But if you want to call from inside the class. Heres the code.
inside a different class as a static function:
onPressed: () {
_MyAppState().startRecord(); //call using the class name.
}
Like this inside your onpressed Statement.
Should work.
Or else what you can do is define the function outside the class. Then use it where ever you want. Like this:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
void startRecord(){
.
.
.
} /// Like here outside the class
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
#override
Widget build(BuildContext context) {
return MaterialApp(.....
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
onPressed: () {
startRecord(); // call Here as follows.
}),
}