Flutter: How to have one tap handler for two widgets? - flutter

I've got an IconButton and Text as the children of a Row widget. Currently when user taps on the calendar icon, its onPressed is handled and a calendar is shown to pick a date. However I want to extend the tapping area and allow the calendar to open even when user taps on the Text widget. What's the best way to achieve this purpose?
Please note that there are more children in this Row. I only want to handle the tap on these two children.

InkWell might do the trick for you.
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
),
);
}
}
In response to the edit:
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: [
Text('Outside the well'),
InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
],
),
),
);
}
}

Wrap the whole Row in GestureDetector as following
GestureDetector(
onTap: () {
// what your want here
}
child: Row(
// ...
),
),

Related

Widgets tree rebuild using FocusScope.of(context).unfocus()

I have this example:
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(30),
child: GestureDetector(
onTap: () {
print('Hide keyboard!!!');
FocusScope.of(context).unfocus();
},
child: Column(
children: [
Text(DateTime.now().toIso8601String()),
TextFormField()
],
),
),
),
),
);
}
When the keyboard appears or is hidden it causes the widget to rebuild. Why does this happen?
Actually, I couldn't find the reason behind the rebuild after using
FocusScope.of(context).unfocus();
But This one will help you to stop rebuild the widget.
FocusManager.instance.primaryFocus.unfocus();
It's working on my application.

Bottom Overflow for ListTile leading widget (Flutter)

I'm trying to add a functionality to each question, represented as a ListTile, so that it can upvote or downvote a question, and show the net votes, just like the one that is used on stack overflow. My current implementation does a bottom overflow for each ListTile.
Card(
child: new Column(
children: <Widget>[
new ListTile(
leading: Column(
children: <Widget>[
FlatButton(
child: Icon(Icons.arrow_drop_up),
onPressed: () {},
),
StreamBuilder<DocumentSnapshot>(
stream: RoomDbService(widget.roomName, widget.roomID)
.getQuestionVotes(widget.questionID),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
print(snapshot.data.data["votes"]);
return Text("${snapshot.data.data["votes"]}");
}
},
),
FlatButton(
child: Icon(Icons.arrow_drop_down),
onPressed: () {},
),
],
), // shows votes of this qn on the left of the tile
title: Text(text),
trailing: FlatButton(
child: Icon(Icons.expand_more),
onPressed: toggleExpansion,
),
)
],
),
);
My previous implementation (which I forgot how it looked like) made it look like a row of an up button, the vote count, and the down button. How do I do it properly?
Check out this example Taking you example I have made some modifications in the code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Card(
child: new Column(
children: <Widget>[
SizedBox(
height: 10,
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
InkWell(onTap: () {}, child: Icon(Icons.arrow_drop_up)),
Text(
"your Text,
style: TextStyle(fontSize: 10),
),
InkWell(onTap: () {}, child: Icon(Icons.arrow_drop_down)),
],
),
Text('You stream text'),
FlatButton(
child: Icon(Icons.expand_more),
onPressed: () {},
),
],
),
)
],
),
)),
);
}
}
Let me know if it works.
You can make custom widget for your desired layout using Row....
But if you still want to use ListTile, then you have to make somechanges in your code,
ListTile's height we can't set as we want, it's depends on subtitle and isThreeLine property.
So you can get some more height if you add subtitle, and with isThreeLine : true, gives your subtitle more height to fit in ListTile....
For your case you need change leading widget....Use InkWell instead of FlatButton....
Make some changes in CircularProgressIndicator.
use small sized icon for upvote/downvote and use small Text for count, otherwise it will overflow again.
See the code below or play with it at DartPad ListTile_StackOverFlow.
Card(
child: ListTile(
leading: Column(
children:[
InkWell(
child: Icon(Icons.arrow_drop_up),
onTap: () {}
),
Container(
height: 8,
width:8,
child: Center(child: CircularProgressIndicator(strokeWidth :2))
),
InkWell(
child: Icon(Icons.arrow_drop_down),
onTap: () {}
),
]
),
title: Text('Titled text'),
trailing: Icon(Icons.more_vert),
),
);
Better solution is use Row and column and make your own custom Widget that looklike ListTile.... see the official document, here you can see an example which has CustomListTile class which creates the custom looking ListTile( which is not directly using ListTile )....
My advise : You should make your custom class as like above Documentation's CustomListTile class

Button OnPressed is not working in Flutter

I have issue with the Raised button click. The method inside OnPressed() is not getting called. Ideally on the OnPressed() method i would like to have the pop up or a slider shown. I have created the example to show the problem currently faced.
The main.dart file calls Screen2()
import 'package:flutter/material.dart';
//import 'package:flutter_app/main1.dart';
import 'screen2.dart';
void main() => runApp(Lesson1());
class Lesson1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen2(),
);
}
}
and in Screen2()i have just have a RaisedButton and OnPressed() it needs to call the function ButtonPressed().
import 'package:flutter/material.dart';
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('Screen 2'),
),
body: Center(
child: RaisedButton(
color: Colors.blue,
child: Text('Go Back To Screen 1'),
onPressed: () {
print('centrebutton');
ButtonPressed();
},
),
),
);
}
}
class ButtonPressed extends StatefulWidget {
#override
_ButtonPressedState createState() => _ButtonPressedState();
}
class _ButtonPressedState extends State<ButtonPressed> {
#override
Widget build(BuildContext context) {
print ('inside button press');
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text('Screen 3'),
),
// create a popup to show some msg.
);
}
}
On clicking the Raised button the print statement ('centerbutton') gets printed.
But the ButtonPressed() method is not getting called .
I am not able to see the print msg('inside button press') in the console. Pl. let me what could be the reason for ButtonPressed method not getting called. Attached the snapshot for your reference.
You are calling a Widget on your RaisedButton's onPressed method. Your widget is getting called but will not render anywhere in the screen.
You should call a function for processing your data in a tap event. But you are calling a widget or say a UI view.
If you want to navigate to the respective screen then you should use navigator.
For ex :
onPressed: () {
print('centrebutton');
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => ButtonPressed()));
},
This could be a cause: If you have an asset as a child of your button, and that asset does not exist, the button onpressed will not work.
Solution: remove the asset.
Example:
return RaisedButton(
splashColor: Colors.grey,
onPressed: () {
},
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage("assets/google_logo.png"), height: 35.0), //remove this line
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 20,
color: Colors.grey,
),
),
)
],
),
),
);

Determine Scroll Widget height

I would like to determine if a 'scrollable' widget indeed needs to scroll. I would ultimately like to show something like 'Scroll for more'. How do I achieve this?
I could use a combination of LayoutBuilder and a ScrollController. However, ScrollController gives me maxScrollExtent and minScrollExtent only after any event - like say for example user trying to scroll
I would like to know during 'build' so that I can determine to show 'Scroll for more' or not depending on the screen dimensions
class _MyHomePageState extends State<MyHomePage> {
int value = 0;
String text = 'You have pushed the button 0 times\n';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
child: Text(text),
),
),
Expanded(
flex: 1,
child: Text('Scroll for more')),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
value += 1;
text = text + 'You have pushed the button $value times \n';
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I would like to dynamically display
Text('Scroll for more'),
only if the SingleChildScrollView needs to be scrolled to view the entire content. Please note, I am just giving the above code as example.
In my real case, I have a StreamBuilder inside a SingleChildScrollView and I cannot determine how much of data is going to be flowing in from the stream. I also do not have any button or tap or any gesture to access the controller and setState
Thanks in advance for any help
class _MyHomePageState extends State<MyHomePage> {
bool showMore = false;
final scrollController = ScrollController();
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
showMore = scrollController.position.maxScrollExtent > 0;
});
});
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
controller: scrollController,
child: SizedBox(height: 650, child: Text('blah')),
),
),
if (showMore) Expanded(flex: 1, child: Text('Scroll for more')),
],
),
),
);
}
}

Child widget of CupertinoPageScaffold is behind CupertinoNavigationBar

I'm trying to put contents below a CupertinoNavigationBar.
But the content Widgets are partially covered by the CupertinoNavigationBar.
I don't understand why the child Column is not vertically offset so that the top isn't covered by the CupertinoNavigationBar.
Below is a screenshot, and my code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PersonalInfoEditor extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
);
}
CupertinoNavigationBar buildNavigationBar(BuildContext context) {
return CupertinoNavigationBar(
trailing: CupertinoButton(
child: Text('Save', style: TextStyle(color: CupertinoColors.activeBlue)),
onPressed: () => Navigator.pop(context),
));
}
}
Wrap your child inside SafeArea :
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: SafeArea(
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
),
);
And don't forget to use CupertinoApp instead of MaterialApp
More info: https://api.flutter.dev/flutter/cupertino/CupertinoPageScaffold-class.html