gestureEvent or PointerEvent on longPressed DialogBox - flutter

How to add tap or longpress (any gestureDector event) on longpressed DialogBox
clicking on icons on popop after showing that, can we swipe on icons and getting action like with click?
hear i'am, using "OverlyEntery" to show dialog, iam using overlyEntry because it will keep the gesture longpress event, it's work fine when longpress show the Dialog and disappear when longpressEnd event occurred, but the problem is after open a dialog Box,
in Dialog box i was not able to add any event, hear i want like instagram image preview.
Thank you.
import 'package:flutter/material.dart';
class Explore extends StatelessWidget {
Explore({Key? key}) : super(key: key);
OverlayEntry _popUp({required String url}) {
return OverlayEntry(
builder: (context) => Dialog(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
padding: EdgeInsets.all(10.0),
color: Colors.black,
child: Center(
child: Text(
"Trip Body name",
style: TextStyle(color: Colors.white),
)),
),
Image.asset(url),
Container(
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onLongPress: () {
print("press1");
},
onLongPressCancel: () {
print("press2");
},
onLongPressDown: (e) {
print("press3");
},
onLongPressMoveUpdate: (d) {
print("press4");
},
child: Container(
child: const Icon(
Icons.favorite,
color: Colors.white,
)),
),
Icon(Icons.save)
],
),
)
],
)));
}
OverlayEntry? _popUpDialog;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: StaggeredGridView.countBuilder(
// controller: _scrollController,
crossAxisCount: 2,
itemCount: 50,
itemBuilder: (BuildContext context, int index) => InkWell(
onTap: () {
},
child: GestureDetector(
// behavior: HitTestBehavior.deferToChild,
onLongPress: () {
_popUpDialog = _popUp(
url: 'assets/images/a' +
((index < 15)
? index.toString()
: index > 20
? '3'
: (index - 10).toString()) +
'.jpeg');
Overlay.of(context)!.insert(_popUpDialog!);
},
onLongPressEnd: (d) {
_popUpDialog!.remove();
},
child: Image.asset((
'assets/images/a' +
((index < 15)
? index.toString()
: index > 20
? '3'
: (index - 10).toString()) +
'.jpeg'
)),
),
staggeredTileBuilder: (int index) => StaggeredTile.count(
1, isForReel(index: index) ? 1.5 : 1), // 2,11,23,30,44
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
)
]
)
);
}
}

Try to add Gesture Detecter on body of DialogBox rather than SubViews.

Related

How to wait for a request to complete using ObservableFuture?

When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');

Update item widget from local database list

I'm using a ready-made local database in my application and the problem is that I can't update one item from the list. If I add a chapter to favorites, then the button's state is updated only after the page is reopened. Likewise, the favorites list is updated only when the page is reopened. Right now when I add/remove favorites, I dynamically load the entire list so that it updates the values, but I only need to update one item, how can I do this using a provider? I didn’t give code examples, because I want to understand exactly the logic of actions
UPD:
My code:
#override
Widget build(BuildContext context) {
return FutureBuilder<List>(
future: _databaseQuery.getAllChapters(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.connectionState == ConnectionState.done &&
snapshot.hasData
? CupertinoScrollbar(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return MainChapterItem(
item: snapshot.data![index],
);
},
),
)
: const Center(
child: CircularProgressIndicator.adaptive(),
);
},
);
}
Item:
final MainChapterItemModel item;
#override
Widget build(BuildContext context) {
return Material(
child: InkWell(
child: Container(
padding: const EdgeInsets.all(8),
child: Row(
children: [
IconButton(
icon: item.favoriteState == 0
? const Icon(CupertinoIcons.bookmark)
: const Icon(CupertinoIcons.bookmark_fill),
splashRadius: 22,
splashColor: const Color(0xff81b9b0),
onPressed: () {
context.read<BookmarkButtonState>().addRemoveChapterBookmark(
item.favoriteState == 0 ? 1 : 0, item.id);
},
),
const SizedBox(
width: 8,
),
Flexible(
child: ListTile(
contentPadding: EdgeInsets.zero,
title: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
item.chapterNumber,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
subtitle: Html(
data: item.chapterTitle,
style: {
'#': Style(
fontSize: const FontSize(17),
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
),
'small': Style(
fontSize: const FontSize(8),
),
'a': Style(
fontSize: const FontSize(14),
color: Colors.blue,
),
},
),
),
),
],
),
),
onTap: () {},
),
);
}
The problem is that when I add to favorites or delete, the button state is not updated. And in the favorites list, the item is not deleted on click, but it disappears after the page is reopened:
IconButton(
icon: item.favoriteState == 0
? const Icon(CupertinoIcons.bookmark)
: const Icon(CupertinoIcons.bookmark_fill),
splashRadius: 22,
splashColor: const Color(0xff81b9b0),
onPressed: () {
context.read<BookmarkButtonState>().addRemoveChapterBookmark(
item.favoriteState == 0 ? 1 : 0, item.id);
},
),
Provider code:
final DatabaseQuery _databaseQuery = DatabaseQuery();
DatabaseQuery get getDatabaseQuery => _databaseQuery;
addRemoveChapterBookmark(int state, int chapterId) {
_databaseQuery.addRemoveFavoriteChapter(state, chapterId);
notifyListeners();
I solved the problem by signing all lists to listen to databaseQuery in the provider:
future: context.watch<BookmarkButtonState>().getDatabaseQuery.getAllChapters(),

How to make just one ExpansionPanel, in an ExpansionPanelList different to the others? flutter

As the question suggests I have an ExpansionPanelList, one ExpansionPanel (the last one or the 7th one) should have 2 additional buttons, but how can I add them just in this one last panel & not in all the others as well?
This is the code of my whole Expansion panel, as Im not sure where you have to add the behaviour, but guessing in the body of the ExpansionPanel (close to line 40):
class ExpansionList extends StatefulWidget {
final Info info;
const ExpansionList({
Key key,
this.info,
}) : super(key: key);
#override
_ExpansionListState createState() => _ExpansionListState();
}
class _ExpansionListState extends State<ExpansionList> {
Widget _buildListPanel() {
return Container(
child: Theme(
data: Theme.of(context).copyWith(
cardColor: Color(0xffDDBEA9),
),
child: ExpansionPanelList(
dividerColor: Colors.transparent,
elevation: 0,
expansionCallback: (int index, bool isExpanded) {
setState(() {
infos[index].isExpanded = !isExpanded;
});
},
children: infos.map<ExpansionPanel>((Info info) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: !isExpanded
? Text(
info.headerValue,
) //code if above statement is true
: Text(
info.headerValue,
textScaleFactor: 1.3,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
);
},
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
color: Color(0xffFFE8D6),
borderRadius: BorderRadius.circular(25)),
child: Column(
children: [
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(left: 40.0,),
itemCount: info.expandedValueData.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(info.expandedValueData[index].title,
style: TextStyle(
decoration: info.expandedValueData[index]
.completed
? TextDecoration.lineThrough
: null)),
value: info.expandedValueData[index].completed,
onChanged: (value) {
setState(() {
// Here you toggle the checked item state
infos.firstWhere(
(currentInfo) => info == currentInfo)
..expandedValueData[index].completed =
value;
});
});
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 20,
);
},
),
Row(children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.16),
Text("Abschnitt bis zum Neustart löschen"),
SizedBox(
width: MediaQuery.of(context).size.width * 0.11),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
infos.removeWhere(
(currentInfo) => info == currentInfo);
});
},
)
]),
],
),
),
),
isExpanded: info.isExpanded);
}).toList(),
),
),
);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
child: _buildListPanel(),
),
);
}
}
Thanks for suggestions!
Hi Just add a field (if you already do not have one) in the info object that will allow you to change the widget that is inflated based on that field.
For example
...
children: infos.map<ExpansionPanel>((Info info) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return info.type == TYPE_A ? TypeAWidgetHeader(info) : TypeBWidgetHeader(info);
body: info.type == TYPE_A ? TypeAWidgetBody(info) : TypeBWidgetBody(info);
...

Return variable from current screen to previous screen

So I am implementing a 'settings' view in my Flutter app. The idea is all settings will appear in a ListView, and when the user will click on a ListTile, a showModalBottomSheet will pop where the user will be able to manipulate the setting. The only problem I am having is I am unable to migrate the showModalBottomSheet to a separate class as I cannot make the new function (outside the class) return the manipulated setting variable. This has lead to a messy code, all in a single class.
class Page extends StatefulWidget {
Page({Key key}) : super(key: key);
#override
_Page createState() => _Page();
}
class _Page extends State<Page> {
var value;
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text("Age"),
trailing: Text(value),
onTap: () {
setState(() {
value = _valueSelector(); // This doesn't work, but to give an idea what I want
});
},
),
],
);
}
}
int _valueSelector(context) { // Doesn't return
var age = 0;
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Wrap(
children: [
Column(
children: <Widget>[
Slider(
value: age.toDouble(),
min: 0,
max: 18,
divisions: 18,
onChanged: (value) {
setState(() {
age = value.toInt();
});
},
),
],
),
],
);
});
},
).whenComplete(() {
return age; // Not sure if return is supposed to be here
});
}
How can I implement showModalBottomSheet in a separate class and just make it return the variable representing the setting chosen by the user?
You can try the below code,
First, create a class custom_bottom_sheet.dart and add the below code. You can use it everywhere in the project. And also use this library modal_bottom_sheet: ^0.2.0+1 to get the showMaterialModalBottomSheet.
customBottomSheet(BuildContext context, {#required Widget widget}) async {
return await showMaterialModalBottomSheet(
context: context,
backgroundColor: AppColors.transparent_100,
barrierColor: AppColors.black_75,
isDismissible: false,
enableDrag: true,
builder: (_, ScrollController scrollController) {
return widget;
},
);
}
Sample example code:
Create another class called bottom_sheet_example.dart and add the below code.
class BottomSheetExample {
static Future getSheet(BuildContext _context,
{ValueChanged<bool> onChanged}) async {
await customBottomSheet(
_context,
widget: SafeArea(
child: Container(
padding: EdgeInsets.only(left: 40.0, right: 40.0),
height: 170.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(27.0),
topRight: Radius.circular(27.0))),
child: Container(
padding: EdgeInsets.only(top: 32),
child: Column(
children: [
Text("Were you at Queen Victoria Building?"),
SizedBox(height: 48),
Row(
children: [
Expanded(
child: RaisedButton(
child: Text("No"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(false);
},
),
),
SizedBox(width: 18),
Expanded(
child: RaisedButton(
child: Text("Yes"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(true);
},
),
),
],
),
SizedBox(height: 24),
],
),
),
)),
);
}
}
Button click to show the bottom sheet
#override
Widget build(BuildContext context) {
return Scaffold(
body: yourBodyWidget(),
bottomNavigationBar: Container(
height: 40,
width: double.infinity,
child: FlatButton(
onPressed: () {
/// call BottomSheetExample class
BottomSheetExample.getSheet(
context,
onChanged: (bool result) async {
///
/// add your code
},
);
},
child: Text("show bottom sheet")),
),
);
}
In onChanged callback you can return your value(obj/String/num/bool/list).
Thank you!

How to change image view in flutter?

I trying to change view from grid to list view.
I used StaggeredGridView for first view but after click change view button it should change into vertical list view .just like instagram gallery view.
Piece of code:
view changing button:
InkWell(
child:view
?Image.asset('assets/icons/view_by1.png')
:Image.asset('assets/icons/view_by3.png'),
onTap: (){
setState(() {
view = !view;
});
}
)
widget for changing view:
Widget sliverGridWidget(BuildContext context, List<Photos> listPhotoDetail){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: view?2:6, //if view true make it vertical view otherwise grid view
itemCount: listPhotoDetail.length,
itemBuilder: (context, index){
return InkWell(
onLongPress: (){
setState(() {
enable = true;
});
},
child: Container(
decoration: BoxDecoration(
color:Colors.grey[100],
image: DecorationImage(
image: NetworkImage(listPhotoDetail[index].photoDetail.image.fileUrl),
fit: BoxFit.cover
)
),
child:enable? Stack(
children: <Widget>[
Positioned(
bottom: 5,
right: 3,
child: InkWell(
child: listPhotoDetail[index].isSelected
?Image.asset('assets/icons/selected.png')
:Image.asset('assets/icons/select_on_image.png'),
onTap: (){
setState(() {
listPhotoDetail[index].isSelected = !listPhotoDetail[index].isSelected;
count= listPhotoDetail[index].isSelected ?count+1:count-1;
if(listPhotoDetail[index].isSelected){
selectedPhotostList.add(
Photos(
photoDetail:listPhotoDetail[index].photoDetail ,
// contact:contactList[index].contact ,
isSelected:listPhotoDetail[index].isSelected)
);
} else{
selectedPhotostList.removeAt(index);
}
// listPhotoDetail[index].isSelected = ! listPhotoDetail[index].isSelected;
// count = listPhotoDetail[index].isSelected ?count+1:count-1;
});
},
)
)
],
)
:Container()
),
);
},
staggeredTileBuilder: (index)
=> StaggeredTile.count(2, 2), //Make size as you want.
mainAxisSpacing: 8.0,
crossAxisSpacing:8.0,
);
}
}
this does not give me satisfied view
means i have total14 images
and gridview looks perfect and shows all 14 images but when pressed changing view button, it
shows only 3 to 4 images as listview.
see :
Here short video
you can take help of this to solve your problem, click on the floating action button to change the view.
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
void main(){
runApp(MaterialApp(home: Example01()));
}
List<Widget> _tiles = const <Widget>[
const _Example01Tile(Colors.green, Icons.widgets),
const _Example01Tile(Colors.lightBlue, Icons.wifi),
const _Example01Tile(Colors.amber, Icons.panorama_wide_angle),
const _Example01Tile(Colors.brown, Icons.map),
const _Example01Tile(Colors.deepOrange, Icons.send),
const _Example01Tile(Colors.indigo, Icons.airline_seat_flat),
const _Example01Tile(Colors.red, Icons.bluetooth),
const _Example01Tile(Colors.pink, Icons.battery_alert),
const _Example01Tile(Colors.purple, Icons.desktop_windows),
const _Example01Tile(Colors.blue, Icons.radio),
const _Example01Tile(Colors.pink, Icons.radio),
const _Example01Tile(Colors.yellow, Icons.radio),
const _Example01Tile(Colors.brown, Icons.map),
const _Example01Tile(Colors.deepOrange, Icons.send),
const _Example01Tile(Colors.indigo, Icons.airline_seat_flat),
];
class Example01 extends StatefulWidget {
#override
_Example01State createState() => _Example01State();
}
class _Example01State extends State<Example01> {
bool isList= false;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Example 01'),
),
body: new Padding(
padding: const EdgeInsets.only(top: 12.0),
child: new StaggeredGridView.countBuilder(
crossAxisCount: 6,
itemCount: _tiles.length,
itemBuilder: (context,i){
return _tiles[i];
},
staggeredTileBuilder: (i)=> isList ?StaggeredTile.count(6,1):StaggeredTile.count(2,2),
mainAxisSpacing: 1.0,
crossAxisSpacing: 1.0,
padding: const EdgeInsets.all(4.0),
)),
floatingActionButton: FloatingActionButton(
child: isList ?Icon(Icons.grid_on):Icon(Icons.list),
onPressed:(){
setState(() {
isList = !isList;
});
},
),
);
}
}
class _Example01Tile extends StatelessWidget {
const _Example01Tile(this.backgroundColor, this.iconData);
final Color backgroundColor;
final IconData iconData;
#override
Widget build(BuildContext context) {
return new Card(
color: backgroundColor,
child: new InkWell(
onTap: () {},
child: new Center(
child: new Padding(
padding: const EdgeInsets.all(4.0),
child: new Icon(
iconData,
color: Colors.white,
),
),
),
),
);
}
}