Related
Trying to implement showBottomSheet into my app, but it is throwing an error: Scaffold.of() called with a context that does not contain a Scaffold.
After searching the web a bit, I added a GlobalKey but that seems like it is not doing the trick. Does anyone have a suggestion?
class _DashboardState extends State<Dashboard> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
int _bottomNavBarCurrentIndex = 0;
dashboardViews.DashboardView dView = new dashboardViews.DashboardView();
List<Widget> _listOffers = new List<Widget>();
Widget _currentView;
void loadDashboardView() {
_currentView = (_bottomNavBarCurrentIndex == 0
? dView.getOfferView(_listOffers, _getOfferData)
: dView.getOrdersView());
}
_showInfoSheet() {
showBottomSheet(
context: _scaffoldKey.currentContext,
builder: (context) {
return Text('Hello');
});
}
Future _getOfferData() async {
loadDashboardView();
List<Widget> _resultsOffers = new List<Widget>();
SharedPreferences prefs = await SharedPreferences.getInstance();
String _token = prefs.getString('token');
final responseOffers =
await http.get(globals.apiConnString + 'GetActiveOffers?token=$_token');
if (responseOffers.statusCode == 200) {
List data = json.decode(responseOffers.body);
for (var i = 0; i < data.length; i++) {
_resultsOffers.add(GestureDetector(
onTap: () => _showInfoSheet(),
child: Card(
child: Padding(
padding: EdgeInsets.all(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(children: <Widget>[
Expanded(
flex: 5,
child: Text('${data[i]['Title']}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: globals.themeColor4))),
Expanded(
flex: 3,
child: Row(children: <Widget>[
Icon(Icons.access_time,
size: 15, color: Colors.grey),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 10, 0),
child: Text('11:30 PM',
style:
TextStyle(color: Colors.black))),
])),
Expanded(
flex: 1,
child: Row(children: <Widget>[
Icon(Icons.local_dining,
size: 15, color: Colors.grey),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 0, 0),
child: Text('${i.toString()}',
style:
TextStyle(color: Colors.black))),
])),
]),
Padding(padding: EdgeInsets.all(10)),
Row(children: <Widget>[
Text(
'Created May 2, 2019 at 2:31 PM',
style: TextStyle(color: Colors.grey[600]),
textAlign: TextAlign.start,
)
])
])))));
}
}
setState(() {
_listOffers = _resultsOffers;
});
loadDashboardView();
}
}
void _bottomNavBarTap(int index) {
setState(() {
_bottomNavBarCurrentIndex = index;
loadDashboardView();
});
}
void pullRefresh() {
_getOfferData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.grey[200],
body: _currentView,
bottomNavigationBar: BottomNavigationBar(
onTap: _bottomNavBarTap,
currentIndex: _bottomNavBarCurrentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.near_me), title: Text('OFFERS')),
BottomNavigationBarItem(
icon: Icon(Icons.broken_image), title: Text('ORDERS'))
],
),
);
}
}
Hope someone can point me in the right direction, thanks!
EDIT: Here is one of the links I looked at with the same problem, and I tried setting the builder context to c as suggested but didn't work: https://github.com/flutter/flutter/issues/23234
EDIT 2: Adding screenshot that shows the variable contents when I'm getting the Scaffold.of() called with a context that does not contain a Scaffold error.
The reason you're getting this error is because you're calling Scaffold during its build process, and thus your showBottomSheet function can't see it. A workaround this problem is to provide a stateful widget with a Scaffold, assign the Scaffold a key , and then pass it to your Dashboard (I assumed it is the name of your stateful widget from your state class). You don't need to assign a key to the Scaffold inside the build of _DashboardState.
This is the class where you provide a Scaffold with key:
class DashboardPage extends StatefulWidget {
#override
_DashboardPageState createState() => _DashboardPageState() ;
}
class _DashboardPageState extends State<DashboardPage> {
final GlobalKey<ScaffoldState> scaffoldStateKey ;
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldStateKey,
body: Dashboard(scaffoldKey: scaffoldStateKey),
);
}
Your original Dashboard altered :
class Dashboard extends StatefulWidget{
Dashboard({Key key, this.scaffoldKey}):
super(key: key);
final GlobalKey<ScaffoldState> scaffoldKey ;
#override
_DashboardState createState() => new _DashboardState();
}
Your _DashboardState with changes outlined :
class _DashboardState extends State<Dashboard> {
int _bottomNavBarCurrentIndex = 0;
dashboardViews.DashboardView dView = new dashboardViews.DashboardView();
List<Widget> _listOffers = new List<Widget>();
Widget _currentView;
void loadDashboardView() {
_currentView = (_bottomNavBarCurrentIndex == 0
? dView.getOfferView(_listOffers, _getOfferData)
: dView.getOrdersView());
}
_showInfoSheet() {
showBottomSheet(
context: widget.scaffoldKey.currentContext, // referencing the key passed from [DashboardPage] to use its Scaffold.
builder: (context) {
return Text('Hello');
});
}
Future _getOfferData() async {
loadDashboardView();
List<Widget> _resultsOffers = new List<Widget>();
SharedPreferences prefs = await SharedPreferences.getInstance();
String _token = prefs.getString('token');
final responseOffers =
await http.get(globals.apiConnString + 'GetActiveOffers?token=$_token');
if (responseOffers.statusCode == 200) {
List data = json.decode(responseOffers.body);
for (var i = 0; i < data.length; i++) {
_resultsOffers.add(GestureDetector(
onTap: () => _showInfoSheet(),
child: Card(
child: Padding(
padding: EdgeInsets.all(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(children: <Widget>[
Expanded(
flex: 5,
child: Text('${data[i]['Title']}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: globals.themeColor4))),
Expanded(
flex: 3,
child: Row(children: <Widget>[
Icon(Icons.access_time,
size: 15, color: Colors.grey),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 10, 0),
child: Text('11:30 PM',
style:
TextStyle(color: Colors.black))),
])),
Expanded(
flex: 1,
child: Row(children: <Widget>[
Icon(Icons.local_dining,
size: 15, color: Colors.grey),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 0, 0),
child: Text('${i.toString()}',
style:
TextStyle(color: Colors.black))),
])),
]),
Padding(padding: EdgeInsets.all(10)),
Row(children: <Widget>[
Text(
'Created May 2, 2019 at 2:31 PM',
style: TextStyle(color: Colors.grey[600]),
textAlign: TextAlign.start,
)
])
])))));
}
}
setState(() {
_listOffers = _resultsOffers;
});
loadDashboardView();
}
void _bottomNavBarTap(int index) {
setState(() {
_bottomNavBarCurrentIndex = index;
loadDashboardView();
});
}
void pullRefresh() {
_getOfferData();
}
#override
Widget build(BuildContext context) {
// Scaffold key has been removed as there is no further need to it.
return Scaffold(
backgroundColor: Colors.grey[200],
body: _currentView,
bottomNavigationBar: BottomNavigationBar(
onTap: _bottomNavBarTap,
currentIndex: _bottomNavBarCurrentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.near_me), title: Text('OFFERS')),
BottomNavigationBarItem(
icon: Icon(Icons.broken_image), title: Text('ORDERS'))
],
),
);
}
}
}
I want to change color of every icon after pressing. But all of icons in a ExpandableContainerchange after pressing one of them.
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
Color _iconColor = Colors.white;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
.
.
.
),
new ExpandableContainer(
expanded: expandFlag,
expandedHeight: ...
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
decoration:
new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
child: new ListTile(
title: ...
leading: new IconButton(
icon: Icon(Icons.star, color: _iconColor),
onPressed: () {
setState(() {
_iconColor = _iconColor == Colors.white ? Colors.yellow : Colors.white;
});
},
),
subtitle: ...
),
);
},
itemCount: ...,
))
],
),
);
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.expandedHeight,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
.
.
.
}
Whole code:
import 'package:flutter/material.dart';
import 'data.dart';
void main() {
runApp(new MaterialApp(home: new Home()));
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
appBar: new AppBar(
title: new Text("Expandable List", style: TextStyle(color: Colors.black)),
backgroundColor: Colors.lightGreen,
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ExpandableListView(title: broadcast[index].title, ind: index);
},
itemCount: broadcast.length,
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
final int ind;
const ExpandableListView({this.title, this.ind});
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
Color _iconColor = Colors.white;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue[300],
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.deepOrange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.title,
style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0, color: Colors.black87),
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
expandedHeight: 90.0 * (broadcast[widget.ind].contents.length < 4 ? broadcast[widget.ind].contents.length : 4), // + (0.0 ?: 29.0),
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
decoration:
new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
child: new ListTile(
title: new Text(
broadcast[widget.ind].contents[index],
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.lightGreen),
textAlign: TextAlign.right,
),
leading: new IconButton(
icon: Icon(Icons.star, color: _iconColor),
onPressed: () {
setState(() {
_iconColor = _iconColor == Colors.white ? Colors.yellow : Colors.white;
});
},
),
subtitle: new Text ('${broadcast[widget.ind].team[index]}\n${broadcast[widget.ind].time[index]} ${broadcast[widget.ind].channel[index]}',
textAlign: TextAlign.right, style:TextStyle(color: Colors.white)),
isThreeLine: true,
),
);
},
itemCount: broadcast[widget.ind].contents.length,
))
],
),
);
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double expandedHeight;
final Widget child;
//final Color iconColor;
ExpandableContainer({
#required this.child,
this.expandedHeight,
this.expanded = true,
//this.iconColor,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 100),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : 0.0,
child: new Container(
child: child,
decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.blue)),
),
);
}
}
You need to make the list item a StatefulWidget in which you have the state _iconColor
Stateful List Tile
class StatefulListTile extends StatefulWidget {
const StatefulListTile({this.subtitle, this.title});
final String subtitle, title;
#override
_StatefulListTileState createState() => _StatefulListTileState();
}
class _StatefulListTileState extends State<StatefulListTile> {
Color _iconColor = Colors.white;
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
border: new Border.all(width: 1.0, color: Colors.grey),
color: Colors.black),
child: new ListTile(
title: new Text(
widget?.title ?? "",
style: new TextStyle(
fontWeight: FontWeight.bold, color: Colors.lightGreen),
textAlign: TextAlign.right,
),
leading: new IconButton(
icon: Icon(Icons.star, color: _iconColor),
onPressed: () {
setState(() {
_iconColor =
_iconColor == Colors.white ? Colors.yellow : Colors.white;
});
},
),
subtitle: new Text(widget?.subtitle ?? "",
textAlign: TextAlign.right, style: TextStyle(color: Colors.white)),
isThreeLine: true,
),
);
}
}
Usage
class ExpandableListView extends StatefulWidget {
final String title;
final int ind;
const ExpandableListView({this.title, this.ind});
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
Color _iconColor = Colors.white;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue[300],
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.deepOrange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.title,
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
color: Colors.black87),
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
expandedHeight: 90.0 *
(broadcast[widget.ind].contents.length < 4
? broadcast[widget.ind].contents.length
: 4), // + (0.0 ?: 29.0),
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return StatefulListTile(
title: broadcast[widget.ind].contents[index],
subtitle:
'${broadcast[widget.ind].team[index]}\n${broadcast[widget.ind].time[index]} ${broadcast[widget.ind].channel[index]}',
);
},
itemCount: broadcast[widget.ind].contents.length,
))
],
),
);
}
}
You should make the color property distinct for each element in the ListView, what you are doing is that the color is global and shared among all the icons in the ListView, for this reason all icons are changing their color when one icon is pressed.
class Broadcast {
final String title;
List<String> contents;
List<String> team = [];
List<String> time = [];
List<String> channel = [];
Color iconColor = Colors.white; //initialize at the beginning
Broadcast(this.title, this.contents, this.team, this.time, this.channel); //, this.icon);
}
edit your ExpandableListView
class ExpandableListView extends StatefulWidget {
final int ind;
final Broadcast broadcast;
const ExpandableListView({this.broadcast,this.ind});
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
edit your Home class
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
appBar: new AppBar(
title: new Text("Expandable List", style: TextStyle(color: Colors.black)),
backgroundColor: Colors.lightGreen,
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ExpandableListView(title: broadcast[index], ind: index);
},
itemCount: broadcast.length,
),
);
}
}
edit your _ExpandableListViewState
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue[300],
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.deepOrange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.broadcast.title,
style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0, color: Colors.black87),
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
expandedHeight: 90.0 * (broadcast[widget.ind].contents.length < 4 ? broadcast[widget.ind].contents.length : 4), // + (0.0 ?: 29.0),
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
decoration:
new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
child: new ListTile(
title: new Text(
broadcast[widget.ind].contents[index],
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.lightGreen),
textAlign: TextAlign.right,
),
leading: new IconButton(
icon: Icon(Icons.star, color: widget.broadcast.iconColor),
onPressed: () {
setState(() {
widget.broadcast.iconColor = widget.broadcast.iconColor == Colors.white ? Colors.yellow : Colors.white;
});
},
),
subtitle: new Text ('${broadcast[widget.ind].team[index]}\n${broadcast[widget.ind].time[index]} ${broadcast[widget.ind].channel[index]}',
textAlign: TextAlign.right, style:TextStyle(color: Colors.white)),
isThreeLine: true,
),
);
},
itemCount: broadcast[widget.ind].contents.length,
))
],
),
);
}
}
I am new to flutter this is my problem,
When I select an icon, I am selecting all the other item in the container
I am not able to change the color of container to green when selected
not only on icon but also on container function onpressed
class _AddEmpToProjectState extends State<AddEmpToProject> {
bool _isSelected = false;
Container addEmpListTile() {
return Container(
margin: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 10.0),
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0), color: Colors.white),
child: Container(
child: ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/tom.jpg'),
),
title: Text('Sam'),
subtitle: Text('Site Manager'),
trailing: InkWell(
onTap: () {
_isSelected = !_isSelected;
setState(() {});
},
child: _isSelected
? Icon(
Icons.done,
color: Colors.green,
size: 35.0,
)
: Icon(
Icons.add,
color: Colors.deepPurple,
size: 35.0,
))),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple,
appBar: AppBar(
title: Text('Add Employees'),
elevation: 0.0,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {},
),
IconButton(
icon: Icon(
Icons.done,
color: Colors.white,
),
onPressed: () {},
)
],
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return addEmpListTile();
},
),
);
}
}
when container is tapped change color to green and should move to top
if deselected must return back to the array widget index
this is what i am expected to do
Need to track in some way which item is selected (some sort of identifier), not only the fact that an item is selected (boolean).
One solution can be to use index as tracking number
class _AddEmpToProjectState extends State<AddEmpToProject> {
-bool _isSelected = false;
+Set<int> _selected = Set();
...
class _AddEmpToProjectState extends State<AddEmpToProject> {
Set<int> _selected = Set();
Container addEmpListTile(int index) {
...
onTap: () {
setState(() {
if(_selected.contains(index)) {
_selected.remove(index);
} else {
_selected.remove(index);
};
}
},
child: _selected.contains(index)
? Icon(Icons.done)
: Icon(Icons.add)
}
...
build() {
...
itemBuilder: (BuildContext context, int index) {
return addEmpListTile(index); // <-- add index here
...
}
I have this piece of code which I got from Style clipboard in flutter
showMenu(
context: context,
// TODO: Position dynamically based on cursor or textfield
position: RelativeRect.fromLTRB(0.0, 600.0, 300.0, 0.0),
items: [
PopupMenuItem(
child: Row(
children: <Widget>[
// TODO: Dynamic items / handle click
PopupMenuItem(
child: Text(
"Paste",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.red),
),
),
PopupMenuItem(
child: Text("Select All"),
),
],
),
),
],
);
This code works great, except that the popup that is created is at a fixed position, how would I make it so that it pops up at the mouse/press/finger/cursor position or somewhere near that, kind of like when you want to copy and paste on your phone. (This dialog popup will not be used for copy and pasting)
I was able to solve a similar issue by using this answer:
https://stackoverflow.com/a/54714628/559525
Basically, I added a GestureDetector() around each ListTile and then you use onTapDown to store your press location and onLongPress to call your showMenu function. Here are the critical functions I added:
_showPopupMenu() async {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
await showMenu(
context: context,
position: RelativeRect.fromRect(
_tapPosition & Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
),
items: [
PopupMenuItem(
child: Text("Show Usage"),
),
PopupMenuItem(
child: Text("Delete"),
),
],
elevation: 8.0,
);
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
}
And then here is the full code (you'll have to tweak a few things like the image, and filling in the list of devices):
import 'package:flutter/material.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'dart:core';
class RecentsPage extends StatefulWidget {
RecentsPage({Key key, this.title}) : super(key: key);
final String title;
#override
_RecentsPageState createState() => _RecentsPageState();
}
class _RecentsPageState extends State<RecentsPage> {
List<String> _recents;
var _tapPosition;
#override
void initState() {
super.initState();
_tapPosition = Offset(0.0, 0.0);
getRecents().then((value) {
setState(() {
_recents = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFFFFF),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(height: 25),
Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 40),
child: Center(
child: AutoSizeText(
"Recents",
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 32),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, top: 0),
child: GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Transform.scale(
scale: 2.0,
child: Icon(
Icons.chevron_left,
),
)),
),
],
),
Container(
height: 15,
),
Container(
height: 2,
color: Colors.blue,
),
Container(
height: 10,
),
Flexible(
child: ListView(
padding: EdgeInsets.all(15.0),
children: ListTile.divideTiles(
context: context,
tiles: _getRecentTiles(),
).toList(),
),
),
Container(height: 15),
],
),
),
),
);
}
List<Widget> _getRecentTiles() {
List<Widget> devices = List<Widget>();
String _dev;
String _owner = "John Doe";
if (_recents != null) {
for (_dev in _recents.reversed) {
if (_dev != null) {
_dev = _dev.toUpperCase().trim();
String serial = "12341234";
devices.add(GestureDetector(
onTapDown: _storePosition,
onLongPress: () {
print("long press of $serial");
_showPopupMenu();
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(vertical: 20),
leading: Transform.scale(
scale: 0.8,
child: Image(
image: _myImage,
)),
title: AutoSizeText(
"$_owner",
maxLines: 1,
style: TextStyle(fontSize: 22),
),
subtitle: Text("Serial #: $serial"),
trailing: Icon(Icons.keyboard_arrow_right),
)));
}
}
} else {
devices.add(ListTile(
contentPadding: EdgeInsets.symmetric(vertical: 20),
title: AutoSizeText(
"No Recent Devices",
maxLines: 1,
style: TextStyle(fontSize: 20),
),
subtitle:
Text("Click the button to add a device"),
onTap: () {
print('add device');
},
));
}
return devices;
}
_showPopupMenu() async {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
await showMenu(
context: context,
position: RelativeRect.fromRect(
_tapPosition & Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
),
items: [
PopupMenuItem(
child: Text("Show Usage"),
),
PopupMenuItem(
child: Text("Delete"),
),
],
elevation: 8.0,
);
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
}
Use gesture detector's onTapDown like this
GestureDetector(
onTapDown: (TapDownDetails details) {
showPopUpMenu(details.globalPosition);
},
then in this method we use tap down details to find position
Future<void> showPopUpMenu(Offset globalPosition) async {
double left = globalPosition.dx;
double top = globalPosition.dy;
await showMenu(
color: Colors.white,
//add your color
context: context,
position: RelativeRect.fromLTRB(left, top, 0, 0),
items: [
PopupMenuItem(
value: 1,
child: Padding(
padding: const EdgeInsets.only(left: 0, right: 40),
child: Row(
children: [
Icon(Icons.mail_outline),
SizedBox(
width: 10,
),
Text(
"Menu 1",
style: TextStyle(color: Colors.black),
),
],
),
),
),
PopupMenuItem(
value: 2,
child: Padding(
padding: const EdgeInsets.only(left: 0, right: 40),
child: Row(
children: [
Icon(Icons.vpn_key),
SizedBox(
width: 10,
),
Text(
"Menu 2",
style: TextStyle(color: Colors.black),
),
],
),
),
),
PopupMenuItem(
value: 3,
child: Row(
children: [
Icon(Icons.power_settings_new_sharp),
SizedBox(
width: 10,
),
Text(
"Menu 3",
style: TextStyle(color: Colors.black),
),
],
),
),
],
elevation: 8.0,
).then((value) {
print(value);
if (value == 1) {
//do your task here for menu 1
}
if (value == 2) {
//do your task here for menu 2
}
if (value == 3) {
//do your task here for menu 3
}
});
hope it works
Here is a reusable widget which does what you need. Just wrap your Text or other Widget with this CopyableWidget and pass in the onGetCopyTextRequested. It will display the Copy menu when the widget is long pressed, copy the text contents returned to the clipboard, and display a Snackbar on completion.
/// The text to copy to the clipboard should be returned or null if nothing can be copied
typedef GetCopyTextCallback = String Function();
class CopyableWidget extends StatefulWidget {
final Widget child;
final GetCopyTextCallback onGetCopyTextRequested;
const CopyableWidget({
Key key,
#required this.child,
#required this.onGetCopyTextRequested,
}) : super(key: key);
#override
_CopyableWidgetState createState() => _CopyableWidgetState();
}
class _CopyableWidgetState extends State<CopyableWidget> {
Offset _longPressStartPos;
#override
Widget build(BuildContext context) {
return InkWell(
highlightColor: Colors.transparent,
onTapDown: _onTapDown,
onLongPress: () => _onLongPress(context),
child: widget.child
);
}
void _onTapDown(TapDownDetails details) {
setState(() {
_longPressStartPos = details?.globalPosition;
});
}
void _onLongPress(BuildContext context) async {
if (_longPressStartPos == null)
return;
var isCopyPressed = await showCopyMenu(
context: context,
pressedPosition: _longPressStartPos
);
if (isCopyPressed == true && widget.onGetCopyTextRequested != null) {
var copyText = widget.onGetCopyTextRequested();
if (copyText != null) {
await Clipboard.setData(ClipboardData(text: copyText));
_showSuccessSnackbar(
context: context,
text: "Copied to the clipboard"
);
}
}
}
void _showSuccessSnackbar({
#required BuildContext context,
#required String text
}) {
var scaffold = Scaffold.of(context, nullOk: true);
if (scaffold != null) {
scaffold.showSnackBar(
SnackBar(
content: Row(
children: <Widget>[
Icon(
Icons.check_circle_outline,
size: 24,
),
SizedBox(width: 8),
Expanded(
child: Text(text)
)
],
)
)
);
}
}
}
Future<bool> showCopyMenu({
BuildContext context,
Offset pressedPosition
}) {
var x = pressedPosition.dx;
var y = pressedPosition.dy;
return showMenu<bool>(
context: context,
position: RelativeRect.fromLTRB(x, y, x + 1, y + 1),
items: [
PopupMenuItem<bool>(value: true, child: Text("Copy")),
]
);
}
I am trying to create a list of dismissible cards inside a customscrollview. The cards are getting rendered, but when i swipe the cards to dismiss them , they don't get removed from the list. Below is the code. Please help.
CustomScrollView customScroll = new CustomScrollView(
slivers: <Widget>[
new SliverAppBar(
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
expandedHeight: 90.0,
title: new Text("Test"),
),
new SliverFixedExtentList(
itemExtent: 128.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
return new Dismissible(key: new ObjectKey(objects[index]),
child: widget.widgetAdapter(objects[index]),
onDismissed: (DismissDirection direction) {
setState(() {
this.objects.removeAt(index);
this.reIndex();
});
direction == DismissDirection.endToStart ? print(
"favourite") : print("remove");
},
background: new Container(
color: const Color.fromRGBO(183, 28, 28, 0.8),
child: const ListTile(
leading: const Icon(
Icons.delete, color: Colors.white, size: 36.0)
)
),
secondaryBackground: new Container(
color: const Color.fromRGBO(0, 96, 100, 0.8),
child: const ListTile(
trailing: const Icon(
Icons.favorite, color: Colors.white, size: 36.0)
)
),
);
},
childCount: objects.length,
),
),
]
);
your attempt is basically correct - I have simplified list creation and replaced it in your sample code below - what you are looking for is in the dmiss function # line 35;
import 'package:flutter/material.dart';
class TestDismissCSV extends StatefulWidget {
#override
_TestDismissCSVState createState() => new _TestDismissCSVState();
}
class _TestDismissCSVState extends State<TestDismissCSV> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Dismiss in Cust Scroll V",
theme: new ThemeData(brightness: Brightness.dark),
home: new Scaffold(
body: dmiss(context),
),
);
}
List<TheListClass> _theList;
Widget dmiss(context) {
return new CustomScrollView(slivers: <Widget>[
new SliverAppBar(
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
expandedHeight: 90.0,
title: new Text("Test"),
),
new SliverFixedExtentList(
itemExtent: 128.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
return new Dismissible(
key: new ObjectKey(_theList[index]),
child: new Material(child: new Text(_theList[index].title)),
onDismissed: (DismissDirection direction) {
setState(() {
this._theList.removeAt(index);
//this.reIndex();
});
direction == DismissDirection.endToStart
? print("favourite")
: print("remove");
},
background: new Container(
color: const Color.fromRGBO(183, 28, 28, 0.8),
child: const ListTile(
leading: const Icon(Icons.delete,
color: Colors.white, size: 36.0))),
secondaryBackground: new Container(
color: const Color.fromRGBO(0, 96, 100, 0.8),
child: const ListTile(
trailing: const Icon(Icons.favorite,
color: Colors.white, size: 36.0))),
);
},
childCount: _theList.length,
),
),
]);
}
#override
void initState() {
super.initState();
_theList = new List<TheListClass>();
for (var i = 0; i < 100; i++) {
_theList.add(new TheListClass('List Item ' + i.toString()));
}
}
#override
void dispose() {
super.dispose();
}
}
class TheListClass {
String title;
TheListClass(this.title);
}
List Item dismissed
Happy coding!