Bottom Overflow for ListTile leading widget (Flutter) - 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

Related

Flutter - How can I make 2 buttons be as large as the largest of them?

I have two ElevatedButtons inside a Row widget and I need both of them to be the same length but only as large as the largest button.
Here's my current code:
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {},
child: Text('Some long text'),
),
ElevatedButton(
onPressed: () {},
child: Text('Short'),
),
],
),
),
);
}
}
As you can see, one button is much larger than the other one. Is it possible to make the "Short" button as long as the "Some long text" button?
I figured I could use Expanded to make them the same size but I don't want this because I need them to use the least possible length while still being the same size and not needing to shrink one of them.
Thanks in advance.
I found that you can wrap the Row with the IntrinsicWidth widget (and all of the Rows children with Expanded) and it works as you want, but the description of it says:
This class is relatively expensive. Avoid using it where possible.
IntrinsicWidth(
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {},
child: Text('Some long text'),
),
),
Expanded(
child: ElevatedButton(
onPressed: () {},
child: Text('Short'),
),
),
],
),
)

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

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(
// ...
),
),

Snackbar doesn't show up if onPressed has more code in it

At first I encountered the issue that the Scaffold.of(context) referred to the wrong context. I implemented the Builder widget properly. The error message was gone but the snackbar still doesn't show up. My first thought was that I've done something wrong and tried the other way around and set a key to the Scaffold.
All this referring to the examples and explanations of this thread which definitely make sense: How to properly display a Snackbar in Flutter?
But nothing of this worked. I was wondering why so I tried a little bit around and found out that the Snackbar works when i remove all the other function calls in my onPressed function of the button
import 'package:flutter/material.dart';
import 'package:flutter_complete_guide/providers/cart_provider.dart'
show CartProvider;
import 'package:flutter_complete_guide/providers/order_provider.dart';
//import 'package:flutter_complete_guide/screens/order_screen.dart';
import 'package:flutter_complete_guide/widgets/cart_item.dart';
import 'package:provider/provider.dart';
class CartScreen extends StatelessWidget {
static const routeName = "/cart";
#override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context);
final _scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Your Cart"),
),
body: Builder(
builder: (BuildContext context) => Column(
children: <Widget>[
Card(
margin: EdgeInsets.all(15.0),
child: Padding(
padding: EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Total",
style: TextStyle(fontSize: 20.0),
),
SizedBox(width: 10),
Spacer(),
Chip(
label: Text("\$${cart.totalAmount}"),
backgroundColor: Theme.of(context).primaryColor,
),
FlatButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("Test Snackbar"),
),
);
/*
** If this code gets called, the snackbar does not work anymore **
Provider.of<OrderProvider>(context, listen: false)
.addOrder(
cart.items.values.toList(), cart.totalAmount);
cart.clear();
*/
},
child: Text("Order now"),
),
],
),
),
),
SizedBox(height: 10),
Expanded(
child: ListView.builder(
itemCount: cart.items.length,
itemBuilder: (ctx, index) => CartItem(
id: cart.items.values.toList()[index].id,
productId: cart.items.keys.toList()[index],
title: cart.items.values.toList()[index].title,
price: cart.items.values.toList()[index].price,
quantity: cart.items.values.toList()[index].quantity,
),
),
),
],
),
),
);
}
}
The strange part about that is, I've added another snackbar to another Button and in that onPressed is another function call aswell but this works perfectly fine:
IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
cart.addItem(products.id, products.price, products.title);
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("${products.title} added"),
duration: Duration(seconds: 1),
action: SnackBarAction(
label: "UNDO",
onPressed: () => cart.removeItemSignle(products.id),
),
),
);
},
I clearly have no clue whats going wrong here, does anyone have an idea?

Flutter Scaffold.of(context).openDrawer() doesn't work

I want to open a drawer after pushing on the custom button in BottomMenu I have trouble with Scaffold.of(context).openDrawer(), it doesn't work. My BottomMenu is a separate widget class. As I understand, it doesn't work because it's a separate context. How can I get the right context? Or perhaps someone knows another solution.
Here my code reproducer:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Drawer'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: BottomMenu(),
endDrawer: SizedBox(
width: double.infinity,
child: Drawer(
elevation: 16,
child: Container(
color: Colors.black,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
],
),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Call Drawer form menu reproducer',
)
],
),
),
);
}
}
class BottomMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Wrap(
alignment: WrapAlignment.center,
children: <Widget>[
Divider(color: Colors.black, height: 1),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
borderRadius: new BorderRadius.circular(20.0),
customBorder: Border.all(color: Colors.black),
child: Container(
padding: EdgeInsets.only(
left: 3, right: 6, bottom: 15, top: 11),
child: Row(
children: <Widget>[
Icon(Icons.menu),
Text('Show menu', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
],
),
),
onTap: () {
Scaffold.of(context).openDrawer();
},
),
],
),
),
],
),
);
}
}
In my case, this worked.
return Scaffold(
key: _scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This!
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
leading: IconButton(
icon: Icon(Icons.menu, size: 36),
onPressed: () => _scaffoldKey.currentState.openDrawer(), // And this!
),
),
drawer: DrawerHome(),
....
and _scaffoldKey must be initialized as,
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
under the class.
The problem is that you specified endDrawer on Scaffold yet you're calling Scaffold.of(context).openDrawer().
openDrawer() documentation states:
If the scaffold has a non-null Scaffold.drawer, this function will cause the drawer to begin its entrance animation.
Since your drawer is null, nothing happens.
In contrast, openEndDrawer() informs us:
If the scaffold has a non-null Scaffold.endDrawer, this function will cause the end side drawer to begin its entrance animation.
Since your endDrawer is not null you should use openEndDrawer() method. Alternatively, if you don't care which side the drawer slides in from, you can use drawer instead of endDrawer when building Scaffold.
My problem solved that instead of
Scaffold.of(context).openEndDrawer()
I give key to Scaffold and then I call by state like below
_scaffoldkey.currentState.openEndDrawer()
It solved my problem I hope It also works for you
Scaffold.of(context).openEndDrawer()
The Problem
This issue can occur when you do not use the correct BuildContext when calling Scaffold.of(context).openDrawer() (or openEndDrawer()).
Easiest Solution
Simply wrap whatever calls openDrawer() (or openEndDrawer()) with a Builder widget. This will give it a working context.
Minimal Working Example
// your build method
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Builder(builder: (context) { // this uses the new context to open the drawer properly provided by the Builder
return FloatingActionButton(onPressed: (() => Scaffold.of(context).openDrawer()));
}),
drawer: const Drawer(
child: Text("MY DRAWER"),
),
);
}
Similar problem here. Clicked on button and nothing happened. The problem is I was using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.
Here is how I solved it:
// body: Column(
// children: <Widget>[
// Row(
// children: <Widget>[
// IconButton(
// icon: Icon(Icons.filter_list),
// onPressed: () => Scaffold.of(context).openEndDrawer(), (wrong context)
// ),
// ],
// ),
// ],
// )
To:
body: Builder(
builder: (context) => Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () => Scaffold.of(context).openEndDrawer(),
),
],
),
],
)),
),
Assign Drawer to drawer property in scaffold. Wrap your specific Widget/Button(where you want to open drawer on its click method) with Builder. Use below method on click property:
enter image description here
Scaffold.of(context).openDrawer();
If you have the appbar widget with an action button to launch the drawer and the drawer is never pushed please remember that you need to define after appbar: ... the endDrawer: YOURAppDrawerWIDGET(), or else using the Scaffold.of(context).openEndDrawer() will not work.
Scaffold(
appBar: AppBar(title: Text(_title)),
endDrawer: AppDrawer(), // <-- this is required or else it will not know what is opening
body: SingleChildScrollView(
///...

onPressed call not defined

Working on an App that will require multiple screens. The below right now shows only two icons, more later, and i need them the be able to go the a corresponding screen when pressed. Everything works but the onPressed function. The error I get is
The named parameter "onPressed" is not defined
Do I have the onPressed function in the wrong spot? I have tried moving it between other functions but I get the same error.
Any help is appreciated
main.dart
import 'package:flutter/material.dart';
import './food_screen.dart';
void main(List<String> args) {
runApp(new MaterialApp(
home : MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title :Text('Main Title'),
backgroundColor: Colors.blue,
),
backgroundColor: Colors.blue[100],
body: Container(
padding: EdgeInsets.all(30.0),
child: GridView.count(
crossAxisCount: 2,
children: <Widget>[
Card(
margin: EdgeInsets.all(8.0),
child: InkWell(
onTap: (){
Navigator.push(context,
MaterialPageRoute(builder: (context)=>FoodScreen())
);
},
splashColor: Colors.blue,
child: Center(
child: Column(
children: <Widget>[
Icon(Icons.fastfood, size: 70.0),
Text("FOOD", style: new TextStyle(fontSize: 28.0))
]
)
),
),
),
Card(
margin: EdgeInsets.all(8.0),
child: InkWell(
onTap: (){},
splashColor: Colors.blue,
child: Center(
child: Column(
children: <Widget>[
Icon(Icons.directions_car, size: 70.0),
Text("VEHILCES", style: new TextStyle(fontSize: 28.0))
],
),
),
),
),
]
)
)
);
}
}
food_screen.dart
import 'package:flutter/material.dart';
import './main.dart';
class FoodScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
);
}
}
Card doesn't support onPressed property, you already have InkWell which has onTap, you can put onPressed method action inside it.
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context)=>FoodScreen())
);
}
Card doesn't have any property of onpressed()
you can add a floating button and Route it to the the second page i.e food_screen.dart
https://api.flutter.dev/flutter/material/Card-class.html
if you want to add a tap on Card Widget just wrap the card with GestureDetector.