I'm doing a web application with Flutter using the google_maps_flutter package. I added a GoogleMap to my web app with onTap event which adds marker on the map. I also want to add a floating action button on top of the map but the problem is that whenever the button is clicked (left click using a mouse on a computer), a new marker is added because onTap is triggered. I don't want that. How can I fix it? The only thing I can think of is onLongPress (right click on a computer) but I'd prefer to avoid it if there is another way by keeping onTap.
void _addLocation(LatLng latLng){
setState(() {
_locationsCount++;
_locations.add(Marker(
markerId: MarkerId(_locationsCount.toString()),
position: latLng,
));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 8.0,
),
onTap: _addLocation,
markers: Set<Marker>.of(_locations),
),
Text(
'Number of locations: $_locationsCount',
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _calculate,
backgroundColor: Colors.green,
label: const Text('Calculate'),
icon: const Icon(Icons.calculate),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
This is a common problem in Flutter web described in details in this issue :
https://github.com/flutter/flutter/issues/72273
In a nutshell to solve your problem you should add this package to your project :
https://pub.dev/packages/pointer_interceptor
Then wrap FloatingActionButton with the PointerInterceptor widget.
The finale code should look like this :
floatingActionButton: PointerInterceptor(
child: FloatingActionButton.extended(
onPressed: _calculate,
backgroundColor: Colors.green,
label: const Text('Calculate'),
icon: const Icon(Icons.calculate),
),
),
Related
How can I open SpeedDial in a horizontal way in my project like this?
If there are other libraries, that is fine.
You can also create your own custom floating action button. But since you are open to packages too writing an answer in which you can achieve it using a package
Try https://pub.dev/packages/custom_floating_action_button
Add this to pubspec.yaml
custom_floating_action_button: ^0.0.3
You can wrap the scaffold with CustomFloatingActionButton
//add custom floating action button on top of your scaffold
//minimum 2 and maximum 5 items allowed
#override
Widget build(BuildContext context) {
return CustomFloatingActionButton(
body: Scaffold(
appBar: AppBar(
title: const Text('appbar title'),
),
body: Container(),
),
options: const [
CircleAvatar(
child: Icon(Icons.height),
),
CircleAvatar(
child: Icon(Icons.title),
),
//All widgets that should appear ontap of FAB.
],
type: CustomFloatingActionButtonType.horizontal,
openFloatingActionButton: const Icon(Icons.add),
closeFloatingActionButton: const Icon(Icons.close),
);
}
I'm working on Flutter project and I'm trying to change the PDF view background color to white but some reason the color is not apply even when I wrap with other widget so I would be really appreciated If I can get any help or suggestion.
Basically I want this to be white too. I'm not sure it's possible but it would be awesome if I can get any suggestion.
body: SfPdfViewer.asset(
'assets/data/songs.pdf',
initialZoomLevel: 3.0,
enableDoubleTapZooming: true,
initialScrollOffset: Offset.fromDirection(10),
controller: _pdfViewerController,
pageLayoutMode: PdfPageLayoutMode.single,
pageSpacing: 4,
canShowScrollHead: false,
onDocumentLoaded: (details) {
_pdfViewerController.jumpToPage(widget.pageNumber);
},
),
final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Syncfusion Flutter PdfViewer'),
actions: <Widget>[
IconButton(
icon: Icon(
Icons. bookmark,
color: Colors.white,
),
onPressed: () {
_pdfViewerKey.currentState?.openBookmarkView();
},
),
],
),
body: SfPdfViewer.network(
'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf',
key: _pdfViewerKey,
),
);
}
you can use scaffold widget and give background color
more information
to achieve this
currently I use this code below
Slidable(
child: ProductListItem(product: product),
endActionPane: ActionPane(
motion: const StretchMotion(),
children: [
SlidableAction(
onPressed: (context) {
//
},
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
icon: Icons.edit,
label: "edit".tr(),
),
SlidableAction(
onPressed: (context) {
//
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: Icons.delete,
label: "delete".tr(),
),
],
),
),
I need to swipe in the right side to open that endPaneActions.
what I want is ....
if I tap that list item, then it will programmatically open that endPaneActions. how to do that?
from the tutorial on Youtube in here , it seems I can use the code below
final slidable = Slidable.of(context);
final isClosed = slidable.renderingMode == SlidableRenderingMode.none;
if (isClosed) {
slidable.open();
}
but it seems that code is obsolete, I can't find .renderingMode method on version 1.2.0
what is the latest version to programmatically open 'end actions pane' ?
finally I can make tap to open slidable programmatically on version 1.2.0. I got a clue from austin's answer on Github
Slidable(
child: LayoutBuilder( // <---- add LayoutBuilder
builder: (contextFromLayoutBuilder, constraints) {
return GestureDetector( // <--- add Gesture Detector
child: YourListItem(), // <--- your list item in here
onTap: () {
final slidable = Slidable.of(contextFromLayoutBuilder);
slidable?.openEndActionPane(
duration: const Duration(milliseconds: 500),
curve: Curves.decelerate,
);
},
);
},
),
endActionPane: ActionPane( // <--- I use endActionPane in here
motion: const StretchMotion(),
children: [],
),
)
as you can see, I add LayoutBuilder and GestureDetector inside the Slidable widget. LayoutBuilder is used to get the 'latest' context inside the Slidable widget, so the slidable value will not be null.
NOTE:
I use endActionPane here, so I use openEndActionPane method. if you use startActionPane, then you should use openStartActionPane method instead
I am new to flutter, and I am building an app that has BottomAppBar which takes the property bottomNavigationBar: of Scaffold() for my home_screen, because I needed it to be at the bottom of the screen and persistent throughout the pages, and I also need a BottomNavigationBar to be persistent also at the top of BottomAppBar, but I can't make that happen because BottomAppBar already takes the bottomNavigationBar: property.
How can I make my BottomNavigationBar persistent alongside my BottomAppBar?
Note: I am using PageView() to scroll through my pages and it will be controlled by the BottomNavigationBar
Edit: attached here is the UI that I am trying to achieve
code snippet:
import 'package:flutter/material.dart';
//screens
import 'package:timewise_flutter/screens/calendar_screen.dart';
import 'package:timewise_flutter/screens/covey_quadrants_screen.dart';
import 'package:timewise_flutter/screens/kanban_screen.dart';
import 'package:timewise_flutter/screens/todo_list_screen.dart';
class OverviewScreen extends StatefulWidget {
static const String id = 'overview_screen';
//const OverviewScreen({Key? key}) : super(key: key);
#override
_OverviewScreenState createState() => _OverviewScreenState();
}
class _OverviewScreenState extends State<OverviewScreen> {
PageController _pageController = PageController(initialPage: 2);
int _bottomNavBarCurrentIndex = 2;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
bottomNavigationBar: SafeArea(
child: BottomAppBar(
elevation: 16.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
tooltip: 'Menu',
icon: Icon(Icons.menu_rounded),
onPressed: () {
print('menu icon pressed!');
//TODO: show bottom modal bottom sheet
},
),
IconButton(
tooltip: 'Pomodoro Timer',
icon: Icon(Icons.hourglass_empty_rounded),
onPressed: () {
print('pomo icon pressed!');
//TODO: show pomodoro timer modal bottom sheet
},
),
IconButton(
tooltip: 'Add',
icon: Icon(Icons.add_circle_outline_outlined),
onPressed: () {
print('add icon pressed!');
//TODO: show add task modal bottom sheet
},
),
],
),
);,
),
body: PageView(
controller: _pageController,
onPageChanged: (page) {
setState(() {
_bottomNavBarCurrentIndex = page;
});
},
children: [
CalendarScreen(),
ToDoListScreen(),
SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Overview Screen'),
BottomNavigationBar(
currentIndex: _bottomNavBarCurrentIndex,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
elevation: 0.0,
iconSize: 16.0,
selectedItemColor: Colors.black,
unselectedItemColor: Colors.grey,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today_rounded),
label: 'Calendar',
tooltip: 'Calendar',
),
BottomNavigationBarItem(
icon: Icon(Icons.checklist_rounded),
label: 'To-Do',
tooltip: 'To-Do List',
),
BottomNavigationBarItem(
icon: Icon(Icons.panorama_fish_eye_rounded),
label: 'Overview',
tooltip: 'Overview',
),
BottomNavigationBarItem(
icon: Icon(Icons.border_all_rounded),
label: 'Covey\'s 4 Quadrants',
tooltip: 'Covey\'s 4 Quadrants',
),
BottomNavigationBarItem(
icon: Icon(Icons.view_column_rounded),
label: 'Kanban Board',
tooltip: 'Kanban Board',
),
],
onTap: (index) {
setState(() {
_bottomNavBarCurrentIndex = index;
_pageController.jumpToPage(index);
});
},
),
],
),
),
),
CoveyQuadrantsScreen(),
KanbanScreen(),
],
),
);
}
}
Unfortunately, this is not a standard way in which the mobile app UI should be designed. This will result in bad user experience.
What if user accidently touches on NavigationBar instead of
AppBar. You will be taken to the new screen and action that I
performed there will be lost or need to handle.
So proper UI guidelines should be met, while we design and develop for the mobile app. Based on guidelines from material.io
Bottom app bars should be used for:
Mobile devices only
Access to a bottom navigation drawer
Screens with two to five actions
Bottom app bars shouldn't be used for:
Apps with a bottom navigation bar
Screens with one or no actions
Refer this link for more useful information about the UI and UX guidelines https://material.io/
I would suggest making a scaffold() with the bottomNavigationBar() as you did. Then you could create a list of Container() objects each representing a different page. For your PageView I'm assuming you have done that, if not then that's the way to do it. Then you could cycle through your pages by setting the body: property of the scaffold to myPages[_currentIndex] or something like that.
Additionally: Like the comment asks, I am also not sure why you would want both BottomNavigationBar and BottomAppBar they both do exactly the same thing. In either case the process is the same as what I described above.
I was trying to make a list of chips which user can reorder by doing drag and drop gesture,
Here is sample code which you can execute to see the issue,
As being told, Chip class need a Material ancestor, so what is solution to this? Have to keep Chip wrapped with Card all the time?
Error:
The following assertion was thrown building Chip(dirty):
No Material widget found.
Chip widgets require a Material widget ancestor.
Code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Wrap(
direction: Axis.horizontal,
children: List.generate(_counter, (i) {
var chip = Chip(
backgroundColor: Colors.blue,
label: Text('item ${i}'),
);
return Draggable(
child: chip,
feedback: chip,
childWhenDragging: Container(),
);
}),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
note: I have modified the default click count template to demonstrate my issue
You can wrap it inside a FloatingActionButton.
var chip = FloatingActionButton(
child: Chip(
backgroundColor: Colors.blue,
label: Text('item $i'),
),
);
Hope it helps!
I had encountered the same issue. You can not use chips in the feedback of draggable. What I did is, wrapped my chip in Material with transparent background. The transparent background helps to remove the extra style that gets added from the Material widget.
feedback: Material(
color: Colors.transparent,
child: Chip(
// your code for the Chip
)
)