Related
I'm doing a project in Flutter in which I'm getting live bit rate using a API and I'm getting my rate but can't display on my screen its say it null..! code below:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'coin_data.dart';
import 'dart:io' show Platform;
import 'networking.dart';
class PriceScreen extends StatefulWidget {
#override
_PriceScreenState createState() => _PriceScreenState();
}
class _PriceScreenState extends State<PriceScreen> {
BitNetwork bitNetwork = BitNetwork('$BitCoinURL/BTC/USD?apikey=$BitCoinKey');
int bitRate;
void getCurrentBitRate() async {
dynamic bitData = await bitNetwork.getData();
double temp = bitData['rate'];
bitRate = temp.toInt();
print(bitRate);
}
String selectedCurrency = 'USD';`enter code here`
#override
Widget build(BuildContext context) {
getCurrentBitRate();
return Scaffold(
appBar: AppBar(
title: Text('Coin Ticker'),
),`enter code here`
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0),
child: Card(
color: Colors.lightBlueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 28.0),
child: Text(
'1 BTC = $bitRate USD',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
),
),
),
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: Platform.isIOS ? iOSPicker() : androidDropdown()),
],
),
);
}
}
answer in console:
I/flutter (14181): 47131
I/flutter (14181): 47131
I/flutter (14181): 47129
output on screen is = 1 BTC = null USD. => ????
You need to wait for currency loading, wrap your widget to FutureBuilder:
Future<int> getCurrentBitRate() async {
dynamic bitData = await bitNetwork.getData();
double temp = bitData['rate'];
return temp.toInt();
}
// build method
child: FutureBuilder<int>(
future: getCurrentBitRate(),
builder (context, snapshot) {
if (snapshot.hasData) {
final bitRate = snapshot.data;
return Column(
// Your column here.
);
}
return CircularProgressIndicator();
}
),
Also, you can find more information about how to work with async features here and read more about FutureBuilder here.
The problem is you're not awaiting getCurrentBitRate() and you are also calling it in your build method. Only UI code should be in the build method. What I recommend you do is override initState() and call it in there (Still can't await it, but it will be called before build);
#override
initState(){
getCurrentBitRate();
super.initState();
}
This will help with your issue, but it's not the best solution. I recommend looking up tutorials on some external state management system, such as BLoC, Provider and/or RxDart. This will make situations like this much easier to debug.
The bitRate value is null because you are calling it in build function & your method getCurrentBitRate() is an async method, which means that the method will wait to get the value but till then your build method would already finish rendering the widgets with bitRate value still null.
There are multiple ways to fix this but the one I would recommend is as follows:
Call your method getCurrentBitRate() in initState method & remove it from the build function as it is the first method that runs in your widget & use setState so that updated value of bitRate is shown in your widget.
class _PriceScreenState extends State<PriceScreen> {
BitNetwork bitNetwork = BitNetwork('$BitCoinURL/BTC/USD?apikey=$BitCoinKey');
int bitRate;
#override
void initState() {
super.initState();
getCurrentBitRate(); // Call it in initState & use setState
}
void getCurrentBitRate() async {
dynamic bitData = await bitNetwork.getData();
double temp = bitData['rate'];
bitRate = temp.toInt();
print(bitRate);
if (mounted) { // <--- mounted property checks whether your widget is still present in the widget tree
setState((){}); // Will update the UI once the value is retrieved
}
}
String selectedCurrency = 'USD';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Coin Ticker'),
),`enter code here`
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0),
child: Card(
color: Colors.lightBlueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 28.0),
child: Text(
'1 BTC = $bitRate USD',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
),
),
),
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: Platform.isIOS ? iOSPicker() : androidDropdown()),
],
),
);
}
}
It's null because when build() is called, getCurrentBitRate() didn't complete it's job yet.
For those operations FutureBuilder is one of the best widget. It just needs a future, and a builder to declare what to do after the data received.
// CHANGE TO FUTURE STYLE
Future<Int> getCurrentBitRate() async {
dynamic bitData = await bitNetwork.getData();
double temp = bitData['rate'];
bitRate = temp.toInt();
print(bitRate);
return bitRate;
}
Then change build structure to this
// DECLARE A FUTURE FOR getCurrentBitRate()
Future _future;
initState(){
_future = await getCurrentBitRate();
super.initState();
}
#override
Widget build(BuildContext context) {
// getCurrentBitRate(); REMOVE THIS LINE
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
if(snapshot.hasData){
// YOUR DATA IS READY
double temp = snapshot.data['rate'];
// JUST CONTINUE REST OF ORIGINAL CODE BELOW
return Scaffold(
appBar: AppBar(
title: Text('Coin Ticker'),
),
...
}
}
);
This question already has answers here:
Scaffold.of() called with a context that does not contain a Scaffold
(15 answers)
Closed 2 years ago.
I am new to Flutter, i am creating my login Screen and i want to show a Snackbar in my screen in case of OnPress of Raised Button, But i am unable to show this message to my app. How to resolve this Problem.
I also attached the error description in following to understand the main thing, i don't know how to manage it.
I used Material Button and Flat Button instead of Raised Button but Problem did not resolved.
Image
Code
import 'package:flutter/material.dart';
import 'package:flutter_hello/Signup.dart';
main() {
runApp(MaterialApp(
// theme: ThemeData(primarySwatch: Colors.red, brightness: Brightness.light),
title: "Umar",
home: new Login(),
));
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(28),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FlutterLogo(
size: 150,
colors: Colors.red,
),
TextFormField(
obscureText: false,
// keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(Icons.person, color: Colors.grey),
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
TextFormField(
obscureText: true,
obscuringCharacter: "*",
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outline, color: Colors.grey),
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
color: Colors.red,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(40, 8, 40, 8),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("Sending Message"),
));
},
child: Text(
"Login",
style: TextStyle(
fontSize: 20.0,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Don't have account?"),
Padding(
padding: EdgeInsets.all(4),
),
GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Signup())),
child: Text(
"SignUp",
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.w600),
),
),
],
)
],
),
),
),
);
}
}
Error
Here is my Error Message shown in Android studio.
Handler: "onTap"
Recognizer:
TapGestureRecognizer#07f5f
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
https://api.flutter.dev/flutter/material/Scaffold/of.html
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
The context used was: Login
state: _LoginState#f029c
When the exception was thrown, this was the stack:
#0 Scaffold.of (package:flutter/src/material/scaffold.dart:1451:5)
#1 _LoginState.build.<anonymous closure> (package:flutter_hello/main.dart:57:28)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#07f5f
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(196.3, 545.3)
finalLocalPosition: Offset(81.3, 20.2)
button: 1
sent tap down
Do it like this,
void showInSnackBar(String value) {
FocusScope.of(context).requestFocus(new FocusNode());
_scaffoldKey.currentState?.removeCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(
value,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
backgroundColor: Colors.blue,
duration: Duration(seconds: 3),
));
}
The Problem is that the context that you are using does not contain a Scaffold, because the Scaffold is created after the context. You can fix this problem by using a Builder-Widget to wrap your content.
Like this:
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) {
// return your body here
},
),
);
}
}
I'm stuck with making a scrollable list like Google Task app when you reach end of the list if any task is completed it shown in another list with custom header as you can see here, I'm using sliver
Widget showTaskList() {
final todos = Hive.box('todos');
return ValueListenableBuilder(
valueListenable: Hive.box('todos').listenable(),
builder: (context, todoData, _) {
int dataLen = todos.length;
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
floating: true,
expandedHeight: 100,
flexibleSpace: Container(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width / 10,
top: MediaQuery.of(context).size.height / 17),
height: 100,
color: Colors.white,
child: Text(
'My Task',
style: TextStyle(fontSize: 30.0, fontWeight: FontWeight.w600),
),
),
),
SliverList(
delegate:
SliverChildBuilderDelegate((BuildContext context, int index) {
final todoData = todos.getAt(index);
Map todoJson = jsonDecode(todoData);
final data = Todo.fromJson(todoJson);
return MaterialButton(
padding: EdgeInsets.zero,
onPressed: () {},
child: Container(
color: Colors.white,
child: ListTile(
leading: IconButton(
icon: data.done
? Icon(
Icons.done,
color: Colors.red,
)
: Icon(
Icons.done,
),
onPressed: () {
final todoData = Todo(
details: data.details,
title: data.title,
done: data.done ? false : true);
updataTodo(todoData, index);
}),
title: Text(
data.title,
style: TextStyle(
decoration: data.done
? TextDecoration.lineThrough
: TextDecoration.none),
),
subtitle: Text(data.details),
trailing: IconButton(
icon: Icon(Icons.delete_forever),
onPressed: () {
todos.deleteAt(index);
}),
),
),
);
}, childCount: dataLen),
),
],
);
});
}
ShowTaskList is called on
Scaffold(
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: showTaskList()
),
]),
),
I tried OffStageSliver to make an widget disappear if no complete todo is present but that did not work and also can not use any other widget on CustomScrollView because that conflict with viewport because it only accept slivers widget.
Here what i have achieved so far
You can try use ScrollController put it on CustomScrollView and listen to it's controller in initState like this :
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
// If it reach end do something here...
}
});
}
I suggest you make bool variable to show your widget, initialize it with false and then after it reach end of controller call setState and make your variable true, which you can't call setState in initState so you have to make another function to make it work like this:
reachEnd() {
setState(() {
end = true;
});
}
Put that function in initState. And make condition based on your bool variabel in your widget
if(end) _yourWidget()
Just like that. I hope you can understand and hopefully this is working the way you want.
I want to make tutorial screen that show to user at beginning. it's like below :
my specific question, how to make some certain elements will show normally and other are opaque ?
also the arrow and text, how to make them point perfectly based on mobile device screen size (mobile responsiveness) ?
As RoyalGriffin mentioned, you can use highlighter_coachmark library, and I am also aware of the error you are getting, the error is there because you are using RangeSlider class which is imported from 2 different packages. Can you try this example in your app and check if it is working?
Add highlighter_coachmark to your pubspec.yaml file
dependencies:
flutter:
sdk: flutter
highlighter_coachmark: ^0.0.3
Run flutter packages get
Example:
import 'package:highlighter_coachmark/highlighter_coachmark.dart';
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
GlobalKey _fabKey = GlobalObjectKey("fab"); // used by FAB
GlobalKey _buttonKey = GlobalObjectKey("button"); // used by RaisedButton
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
key: _fabKey, // setting key
onPressed: null,
child: Icon(Icons.add),
),
body: Center(
child: RaisedButton(
key: _buttonKey, // setting key
onPressed: showFAB,
child: Text("RaisedButton"),
),
),
);
}
// we trigger this method on RaisedButton click
void showFAB() {
CoachMark coachMarkFAB = CoachMark();
RenderBox target = _fabKey.currentContext.findRenderObject();
// you can change the shape of the mark
Rect markRect = target.localToGlobal(Offset.zero) & target.size;
markRect = Rect.fromCircle(center: markRect.center, radius: markRect.longestSide * 0.6);
coachMarkFAB.show(
targetContext: _fabKey.currentContext,
markRect: markRect,
children: [
Center(
child: Text(
"This is called\nFloatingActionButton",
style: const TextStyle(
fontSize: 24.0,
fontStyle: FontStyle.italic,
color: Colors.white,
),
),
)
],
duration: null, // we don't want to dismiss this mark automatically so we are passing null
// when this mark is closed, after 1s we show mark on RaisedButton
onClose: () => Timer(Duration(seconds: 1), () => showButton()),
);
}
// this is triggered once first mark is dismissed
void showButton() {
CoachMark coachMarkTile = CoachMark();
RenderBox target = _buttonKey.currentContext.findRenderObject();
Rect markRect = target.localToGlobal(Offset.zero) & target.size;
markRect = markRect.inflate(5.0);
coachMarkTile.show(
targetContext: _fabKey.currentContext,
markRect: markRect,
markShape: BoxShape.rectangle,
children: [
Positioned(
top: markRect.bottom + 15.0,
right: 5.0,
child: Text(
"And this is a RaisedButton",
style: const TextStyle(
fontSize: 24.0,
fontStyle: FontStyle.italic,
color: Colors.white,
),
),
)
],
duration: Duration(seconds: 5), // this effect will only last for 5s
);
}
}
Output:
You can use this library to help you achieve what you need. It allows you to mark views which you want to highlight and how you want to highlight them.
Wrap your current top widget with a Stack widget, having the first child of the Stack your current widget.
Below this widget add a Container with black color, wrapped with Opacity like so:
return Stack(
children: <Widget>[
Scaffold( //first child of the stack - the current widget you have
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text("Foo"),
Text("Bar"),
],
),
)),
Opacity( //seconds child - Opaque layer
opacity: 0.7,
child: Container(
decoration: BoxDecoration(color: Colors.black),
),
)
],
);
you then need to create image assets of the descriptions and arrows, in 1x, 2x, 3x resolutions, and place them in your assets folder in the appropriate structure as described here: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets
you can then use Image.asset(...) widget to load your images (they will be loaded in the correct resolution), and place these widgets on a different container that will also be a child of the stack, and will be placed below the black container in the children list (the Opacity widget on the example above).
It should be mentioned that instead of an opaque approach the Material-oriented feature_discovery package uses animation and integrates into the app object hierarchy itself and therefore requires less custom highlight programming. The turnkey solution also supports multi-step highlights.
Screenshot (Using null-safety):
Since highlighter_coachmark doesn't support null-safety as of this writing, use tutorial_coach_mark which supports null-safety.
Full Code:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final List<TargetFocus> targets;
final GlobalKey _key1 = GlobalKey();
final GlobalKey _key2 = GlobalKey();
final GlobalKey _key3 = GlobalKey();
#override
void initState() {
super.initState();
targets = [
TargetFocus(
identify: 'Target 1',
keyTarget: _key1,
contents: [
TargetContent(
align: ContentAlign.bottom,
child: _buildColumn(title: 'First Button', subtitle: 'Hey!!! I am the first button.'),
),
],
),
TargetFocus(
identify: 'Target 2',
keyTarget: _key2,
contents: [
TargetContent(
align: ContentAlign.top,
child: _buildColumn(title: 'Second Button', subtitle: 'I am the second.'),
),
],
),
TargetFocus(
identify: 'Target 3',
keyTarget: _key3,
contents: [
TargetContent(
align: ContentAlign.left,
child: _buildColumn(title: 'Third Button', subtitle: '... and I am third.'),
)
],
),
];
}
Column _buildColumn({required String title, required String subtitle}) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Text(subtitle),
)
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Stack(
children: [
Align(
alignment: Alignment.topLeft,
child: ElevatedButton(
key: _key1,
onPressed: () {},
child: Text('Button 1'),
),
),
Align(
alignment: Alignment.center,
child: ElevatedButton(
key: _key2,
onPressed: () {
TutorialCoachMark(
context,
targets: targets,
colorShadow: Colors.cyanAccent,
).show();
},
child: Text('Button 2'),
),
),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
key: _key3,
onPressed: () {},
child: Text('Button 3'),
),
),
],
),
),
);
}
}
Thanks to #josxha for the suggestion.
If you don't want to rely on external libraries, you can just do it yourself. It's actually not that hard.
Using a stack widget you can put the semi-transparent overlay on top of everything. Now, how do you "cut holes" into that overlay that emphasize underlying UI elements?
Here is an article that covers the exact topic: https://www.flutterclutter.dev/flutter/tutorials/how-to-cut-a-hole-in-an-overlay/2020/510/
I will summarize the possibilities you have:
Use a ClipPath
By using a CustomClipper, given a widget, you can define what's being drawn and what's not. You can then just draw a rectangle or an oval around the relevant underlying UI element:
class InvertedClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
return Path.combine(
PathOperation.difference,
Path()..addRect(
Rect.fromLTWH(0, 0, size.width, size.height)
),
Path()
..addOval(Rect.fromCircle(center: Offset(size.width -44, size.height - 44), radius: 40))
..close(),
);
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
Insert it like this in your app:
ClipPath(
clipper: InvertedClipper(),
child: Container(
color: Colors.black54,
),
);
Use a CustomPainter
Instead of cutting a hole in an overlay, you can directly draw a shape that is as big as the screen and has the hole already cut out:
class HolePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black54;
canvas.drawPath(
Path.combine(
PathOperation.difference,
Path()..addRect(
Rect.fromLTWH(0, 0, size.width, size.height)
),
Path()
..addOval(Rect.fromCircle(center: Offset(size.width -44, size.height - 44), radius: 40))
..close(),
),
paint
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Insert it like this:
CustomPaint(
size: MediaQuery.of(context).size,
painter: HolePainter()
);
Use ColorFiltered
This solution works without paint. It cuts holes where children in the widget trees are inserted by using a specific blendMode:
ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.black54,
BlendMode.srcOut
),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
color: Colors.transparent,
),
child: Align(
alignment: Alignment.bottomRight,
child: Container(
margin: const EdgeInsets.only(right: 4, bottom: 4),
height: 80,
width: 80,
decoration: BoxDecoration(
// Color does not matter but must not be transparent
color: Colors.black,
borderRadius: BorderRadius.circular(40),
),
),
),
),
],
),
);
I put a list of widget as action in Scaffold appBar, but they didn't respond when I press them, I have a floatingButton in the scene too and it works perfectly.
appBar: new AppBar(
title: new Text(
widget.title,
style: new TextStyle(
fontFamily: 'vazir'
),
),
centerTitle: true,
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.search),
highlightColor: Colors.pink,
onPressed: _onSearchButtonPressed(),
),
],
),
void _onSearchButtonPressed() {
print("search button clicked");
}
even if I put IconButton in a Row or Column widget , not in appBar, it doesn't work again.
Answer:
thanks to siva Kumar, I had a mistake in calling function , we should call it in this way:
onPressed: _onSearchButtonPressed, // without parenthesis.
or this way:
onPressed: (){
_onSearchButtonPressed();
},
please try with my answer it will work.
appBar: new AppBar(
title: new Text(
widget.title,
style: new TextStyle(
fontFamily: 'vazir'
),
),
centerTitle: true,
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.search),
highlightColor: Colors.pink,
onPressed: (){_onSearchButtonPressed();},
),
],
),
void _onSearchButtonPressed() {
print("search button clicked");
}
Bump into the question while searching for other solution.
The answer should be:
onPressed: _onSearchButtonPressed,
Without the () brackets. Since they carry the same signature, there is no need to wrap them around another anonymous / lambda function.
Actually we need to set the VoidCallback for onPressed property, When we tap on icon that VoidCallback is called .
We also set null if we don't need any response.
class PracticeApp extends StatelessWidget {
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton: new FloatingActionButton(
tooltip: "Add",
child: new Icon(Icons.add),
onPressed: () { setState(); },
),
);
}
}
void setState() {
print("Button Press");
}
We can also directly pass the call back like this
onPressed: () { setState(() { _volume *= 1.1; }); }
Example for null
onPressed: null