For instance: I have a main IconButton using assets/image/button.png as an icon!
so when you click on it, it opens a pop-up window with smaller icons/images using the assets as well and from that pop-up, you can select IconButton to replace the main Image. So if you select one of the pictures from that pop-up it replaces the main Icon to that specific image. How would you implement that if you wanted to use your assets as buttons?
On separate Dart file I have main Image Container:
Container(
width: 100,
padding: EdgeInsets.only(bottom: 50),
child: MainIcon()
),
Code I have tried:
class MainIcon extends StatefulWidget {
const MainIcon({Key? key}) : super(key: key);
#override
State<MainIcon> createState() => _MainIcon();
}
class _MainIcon extends State<MainIcon> {
Widget build(BuildContext context) {
return Container(
child: IconButton(
icon: GetItems(),
iconSize: 150,
onPressed: () {
showShopItems();
},
));
}
//pop-up window for changing icons
Future showShopItems() => showDialog(
context: context,
builder: (context) => AlertDialog(
content: ShopItems(),
backgroundColor: Colors.transparent,
));
//change food icons
Image GetItems() {
if (Bag.isClicked = true) {
return Image.asset('assets/images/bag.png');
}
if (Shampoo.isClicked = true) {
return Image.asset('assets/images/shampoo.png');
}
if (lotion.isClicked == true) {
return Image.asset('assets/images/lotion.png');
} else {
return Image.asset('assets/images/cart.png');
}}}
What each widget button looks like:
class Bag extends StatefulWidget {
static bool isClicked = false;
const Bag({Key? key}) : super(key: key);
#override
State<Bag> createState() => _Bag();
}
class _Bag extends State<Bag> {
Widget build(BuildContext context) {
return Container(
child: IconButton(
icon: Image.asset('assets/images/bag.png'),
iconSize: 70,
onPressed: () {
Bag.isPressed == true;
});
print("bag is clicked");
},
));
}
}
I would try to give each button an id and when clicking on the corresponding button, assign the desired id to the big main button, and then call setState (() {}) to update the widget and it will change its image. Further, I would save this id to the Database and asynchronously load the value every time I log in to the application, you can use Flutter Secure Storage or Shared Preferences as the Database for application settings. If you have something more than just changing the image on the main button, for example, changing the link that the button leads to, then create models, then create a list of models with predefined values and use them.
Related
This issue is related with github #2502.
I am using GetMaterialApp from this package.
I'm not sure if this is a bug or not.
How to make the function in the first dialog useable by using Get.toNamed()?
It happened when using the Get.toNamed().
It works fine with Navigator.push() but I need Get.toNamed for the web app.
The first page has a button that will show the first dialog.
The first dialog will show the order type button list.
When pressing an order type button, the program will find a new order of this type and open the second page with a new order data.
The second page has some work to do and this work will open the second dialog.
After finishing this work, the user will click on the back button back to the first page and find a new order again.
The problem is when the second dialog works on the second page.
The first dialog on the first page will not work.
see video example.
web example.
code example:
import 'package:flutter/material.dart';
import 'package:flutter_test_exam_bug/config/path/page_path.dart';
import 'package:get/get.dart';
Future<void> _showMyDialog({required BuildContext context, required Widget child}) async {
return showDialog<void>(
context: context,
builder: (BuildContext context) => child,
);
}
class PageTest extends StatefulWidget {
const PageTest({Key? key}) : super(key: key);
#override
_PageTestState createState() => _PageTestState();
}
class _PageTestState extends State<PageTest> {
#override
Widget build(BuildContext context) {
Widget dialog_ = Center(
child: ElevatedButton(onPressed: () => Get.toNamed(PagePath.test2), child: const Text("Open second page"))),
openDialogButton_ = ElevatedButton(
onPressed: () => _showMyDialog(context: context, child: dialog_), child: const Text("Open first dialog"));
return Scaffold(body: SafeArea(child: Center(child: openDialogButton_)));
}
}
class PageTest2 extends StatefulWidget {
const PageTest2({Key? key}) : super(key: key);
#override
State<PageTest2> createState() => _PageTest2State();
}
class _PageTest2State extends State<PageTest2> {
ButtonStyle buttonStyle = ElevatedButton.styleFrom(primary: Colors.green);
#override
Widget build(BuildContext context) {
Widget dialog_ = Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context), child: const Text("I am second dialog"), style: buttonStyle)),
openDialogButton_ = ElevatedButton(
onPressed: () => _showMyDialog(context: context, child: dialog_),
child: const Text("Open second dialog"),
style: buttonStyle);
return Scaffold(appBar: AppBar(), body: SafeArea(child: Center(child: openDialogButton_)));
}
}
I think it is a bug.
When opening a dialog, the GETX ROUTE will change to the current page again.
Follow this in https://github.com/jonataslaw/getx/issues/2502
I have a list view and want to edit the Tile's title. When user click the edit icon, text widget change to TextField. Once user tap the textfield, keyboard show and immediately disappeared.
May I know what is the issue?
class EditableListTile extends StatefulWidget {
final Favourite favourite;
final Function onChanged;
final Function onTap;
const EditableListTile(
{Key? key,
required this.favourite,
required this.onChanged,
required this.onTap})
: super(key: key);
#override
_EditableListTileState createState() => _EditableListTileState();
}
class _EditableListTileState extends State<EditableListTile> {
Favourite? favourite;
late bool _isEditingMode;
late TextEditingController _titleEditingController;
#override
void initState() {
super.initState();
favourite = widget.favourite;
_isEditingMode = false;
}
#override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
widget.onTap(favourite);
},
leading: leadingWidget,
title: titleWidget,
trailing: tralingButton,
);
}
Widget get leadingWidget {
return SizedBox(
width: 32,
child: FolderIcon(
color: Theme.of(context).iconTheme.color!,
),
);
}
Widget get titleWidget {
if (_isEditingMode) {
_titleEditingController = TextEditingController(text: favourite?.name);
return TextField(
controller: _titleEditingController,
);
} else {
return Text(favourite!.name);
}
}
Widget get tralingButton {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
(favourite?.isDefault == false)
? (_isEditingMode
? IconButton(
icon: const Icon(Icons.check),
onPressed: saveChange,
)
: IconButton(
icon: const Icon(Icons.edit),
onPressed: _toggleMode,
))
: Container(),
_isEditingMode
? IconButton(
icon: const Icon(Icons.cancel_outlined),
onPressed: cancelChange,
)
: Container()
],
);
}
void _toggleMode() {
setState(() {
_isEditingMode = !_isEditingMode;
});
}
void cancelChange() {
setState(() {
_isEditingMode = !_isEditingMode;
});
}
void saveChange() {
favourite!.name = _titleEditingController.text;
_toggleMode();
widget.onChanged(favourite!);
}
}
you get this error because you initialized the TextEdittingController inside the titleWidget. every time the widget rebuild, create a new instance of TextEdittingController.
on top of your clas change it like this
// late TextEditingController _titleEditingController; <== Change This
TextEditingController _titleEditingController = TextEditingController();
in titleWidget change your code to this.
Widget get titleWidget {
if (_isEditingMode) {
_titleEditingController.text = favourite?.name;
Real culprit is key in ListView.seperate. I used key : UniqueKey(). If I change to ValueKey(state.favourites[index]), it is working now.
I used key : UniqueKey() because I have onDismissed but one of the item, want to trigger onDismissed but don't want to dismissed.
Let's say, item is folder name and if user delete the item, delete the folder and delete all the files under that folder. But one folder is system generated and don't want user to delete that folder but let them to delete files. So we call confirmDismiss and tell the user that it is system generated folder and only will delete files.
But list view don't allow. So I found out the UniqueKey is work
around. So edit is importance. So that I take out UniqueKey.
Like Alex Aung's answer above, they're right that the use of UniqueKey() is a problem .
In my case I had an action button that pushed a page route to the navigator. On the content view (GameView) I had a UniqueKey() set and it was responsible for series of issues with input fields downstream.
Any time I set this back to UniqueKey(), any clicking inside a downstream TextFormField causes the Keyboard open then immediately close.
Widget getActionButton() {
return FloatingActionButton(
onPressed: () {
final Account account = Account("test#test.com");
widget.viewModels.gamesModel.load(account);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GamesView(
key: Key("GameView"), // UniqueKey() is a problem here.
account: account,
viewModels: widget.viewModels,
)
)
);
},
tooltip: 'Save Changes',
child: Icon(Icons.save),
);
}
I want to set the updated text value of button throughout the app, when i click on button its text changes to current time, but when I navigate to other screen, and then come back to the screen where I created a button, it is not showing the updated text.
here is my button widget
String getTime;
//from here i get the current time
void _getTime() {
final String formattedDateTime =
DateFormat('kk:mm:ss a').format(DateTime.now()).toString();
setState(() {
getTime = formattedDateTime;
print("time");
print(getTime);
});
}
String timeInText = "Time in";
Widget _timein() {
//enable- initial case
bool firstCaseFlag = true;
if (getTimeInStatus == false && timeInButtonPressed == true) {
print("i1");
return FlatButton(
color: timeInButtonPressed ? Colors.blue[500] : Colors.blue[200],
textColor: Colors.white,
padding: EdgeInsets.all(15.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(buttonRoundRadius)),
child: Row(children: <Widget>[
Icon(
Icons.timer,
),
Expanded(
child: Text(
timeInText,
textAlign: TextAlign.center,
style: TextStyle(fontSize: textFontSize),
),
),
]),
onPressed: () {
_getTime();
setState(() {
if (firstCaseFlag == true) {
timeInText = getTime; //here i set the button text to current time
timeIn = timeInText;
firstCaseFlag = false;
} else {
}
});
calltimeInApi();
});
Conditions:
There are certain conditions where button will change there state, like i have 2 button namely timein and timeout, initially timein button will be enable to click and timeout will be disable, so if user click on timein button its text change to current time and timeout button will be enable (this is all happening), and if user moved to other screen and come to home screen (where i created timein and timeout buttons) then timein button text should display that time when user click on it.
Problem:
My problem is when I moved to other screen and come to home screen timein button is enabled and not showing the time when i click on it.
please help how i can fix it.
I prefer using statemanagement StateProvider. here is an example just using global variable.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:stack_overflow/exports.dart';
String buttonText = "Click to set";
///for riverpod
///final buttonState = StateProvider((ref) => "Click to set");
class BaseWidget extends StatefulWidget {
const BaseWidget({Key? key}) : super(key: key);
#override
_BaseWidgetState createState() => _BaseWidgetState();
}
class _BaseWidgetState extends State<BaseWidget> {
void _getTime() {
final String formattedDateTime =
DateFormat('kk:mm:ss a').format(DateTime.now()).toString();
setState(() {
buttonText = formattedDateTime;
print("time");
print(buttonText);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
_getTime();
},
child: Text(buttonText),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NextWidget(),
));
},
child: Text("next"),
),
],
),
);
}
}
class NextWidget extends StatelessWidget {
const NextWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("Back"),
),
);
}
}
Use state management like Provider to keep the values and then access anywhere.
Package link: https://pub.dev/packages/provider
Helpful reference: https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro
Like Google Drive, can I create custom menu in Flutter Web application?.
Below the instruction how to implement working context menu called via mouse right button in flutter web app:
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:universal_html/html.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
// Prevent default event handler
document.onContextMenu.listen((event) => event.preventDefault());
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Listener(
child: Icon(
Icons.ac_unit,
size: 48.0,
),
onPointerDown: _onPointerDown,
),
),
);
}
/// Callback when mouse clicked on `Listener` wrapped widget.
Future<void> _onPointerDown(PointerDownEvent event) async {
// Check if right mouse button clicked
if (event.kind == PointerDeviceKind.mouse &&
event.buttons == kSecondaryMouseButton) {
final overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final menuItem = await showMenu<int>(
context: context,
items: [
PopupMenuItem(child: Text('Copy'), value: 1),
PopupMenuItem(child: Text('Cut'), value: 2),
],
position: RelativeRect.fromSize(
event.position & Size(48.0, 48.0), overlay.size));
// Check if menu item clicked
switch (menuItem) {
case 1:
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Copy clicked'),
behavior: SnackBarBehavior.floating,
));
break;
case 2:
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Cut clicked'),
behavior: SnackBarBehavior.floating));
break;
default:
}
}
}
}
The only thing is to do is correct positioning of left top corner of context menu.
Until the open issue is resolved, you can do the following in your main():
import 'dart:html';
void main() {
window.document.onContextMenu.listen((evt) => evt.preventDefault());
// ...
}
Here is the open issue for it: https://github.com/flutter/flutter/issues/31955
You can disable it for a webpage like this:
How do I disable right click on my web page?
You can also listen for Pointer Signal events and render the popup in Flutter:
https://medium.com/#crizantlai/flutter-handling-mouse-events-241108731537
Basically on web for example you would disable the default context menu, and show an Overlay in flutter when you receive the right click pointer signal.
Prevent default contextmenu
Add an oncontextmenu attribute to <html> tag in web/index.html:
<!DOCTYPE html>
<html oncontextmenu="event.preventDefault();">
<head>
...
See also: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#event_handler_attributes
This has the same effect as https://stackoverflow.com/a/64779321/16613821 (window.document is just the <html> tag), but without triggering "Avoid using web-only libraries outside Flutter web plugin packages." warning or using universal_html package.
NOTE: Hot reload won't work for this kind of change, but you can simply refresh(F5) browser.
Add your custom contextmenu
https://github.com/flutter/flutter/pull/74286 doesn't work well for your usecase
This should show up by default on desktop, but only when right clicking on EditableText-based widgets. Right clicking elsewhere does nothing, for now.
This is also purposely not customizable or reusable for now. It was a temporary solution that we plan to expand on.
In general, you can use GestureDetector.onSecondaryTap to detect user's right click.
Thanks for the inspiration BambinoUA. I decided to make my own cross platform class for this.
Works on iOS/Android/Web/Windows/Mac & Linux. Tested.
import 'package:bap/components/splash_effect.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:universal_html/html.dart' as html;
class CrossPlatformClick extends StatefulWidget {
final Widget child;
/**
* Normal touch, tap, right click for platforms.
*/
final Function()? onNormalTap;
/**
* A list of menu items for right click or long press.
*/
final List<PopupMenuEntry<String>>? menuItems;
final Function(String? itemValue)? onMenuItemTapped;
const CrossPlatformClick({Key? key, required this.child, this.menuItems, this.onNormalTap, this.onMenuItemTapped}) : super(key: key);
#override
State<CrossPlatformClick> createState() => _CrossPlatformClickState();
}
class _CrossPlatformClickState extends State<CrossPlatformClick> {
/**
* We record this so that we can use long-press and location.
*/
PointerDownEvent? _lastEvent;
#override
Widget build(BuildContext context) {
final listener = Listener(
child: widget.child,
onPointerDown: (event) => _onPointerDown(context, event),
);
return SplashEffect(
isDisabled: widget.onNormalTap == null,
borderRadius: BorderRadius.zero,
onTap: widget.onNormalTap!,
child: listener,
onLongPress: () {
if (_lastEvent != null) {
_openMenu(context, _lastEvent!);
return;
}
if (kDebugMode) {
print("Last event was null, cannot open menu");
}
},
);
}
#override
void initState() {
super.initState();
html.document.onContextMenu.listen((event) => event.preventDefault());
}
/// Callback when mouse clicked on `Listener` wrapped widget.
Future<void> _onPointerDown(BuildContext context, PointerDownEvent event) async {
_lastEvent = event;
if (widget.menuItems == null) {
return;
}
// Check if right mouse button clicked
if (event.kind == PointerDeviceKind.mouse && event.buttons == kSecondaryMouseButton) {
return await _openMenu(context, event);
}
}
_openMenu(BuildContext context, PointerDownEvent event) async {
final overlay = Overlay.of(context)!.context.findRenderObject() as RenderBox;
final menuItem = await showMenu<String>(
context: context,
items: widget.menuItems ?? [],
position: RelativeRect.fromSize(event.position & Size(48.0, 48.0), overlay.size),
);
widget.onMenuItemTapped!(menuItem);
}
}
The class for standard splash effect touches
import 'package:flutter/material.dart';
class SplashEffect extends StatelessWidget {
final Widget child;
final Function() onTap;
final Function()? onLongPress;
final BorderRadius? borderRadius;
final bool isDisabled;
const SplashEffect({
Key? key,
required this.child,
required this.onTap,
this.isDisabled = false,
this.onLongPress,
this.borderRadius = const BorderRadius.all(Radius.circular(6)),
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (isDisabled) {
return child;
}
return Material(
type: MaterialType.transparency,
child: InkWell(
borderRadius: borderRadius,
child: child,
onTap: onTap,
onLongPress: onLongPress,
),
);
}
}
And how to use it:
return CrossPlatformClick(
onNormalTap: onTapped,
menuItems: [
PopupMenuItem(child: Text('Copy Name', style: TextStyle(fontSize: 16)), value: "copied"),
],
onMenuItemTapped: (item) {
print("item tapped: " + (item ?? "-no-item"));
},
child:
I am following this link,
https://medium.com/…/developing-for-multiple-screen-sizes-a…
to create a master detail ipad application.
I have a scenario, there is a text field and button in detail page. When i change the text field value and press the button, the listview item (in left side) at that specific index also should be updated. can somebody suggest a work around?
You can return the edited object using Navigator.pop(context,object) to the Navigator.push() caller. I wrote an example app for you.
the data class:
class Item {
final String name;
Item(this.name);
}
the home page, where I display the item:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Item item = Item('ali2236');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Text(item.name),
FlatButton(
child: Text('edit'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return ItemEditingPage(
item: item,
callbackFunction: (editedItem){
setState(() {
item = editedItem;
});
},
);
}));
},
),
],
),
),
),
);
}
}
and the editing page:
class ItemEditingPage extends StatefulWidget {
final Item item;
final void Function(Item item) callbackFunction;
const ItemEditingPage({Key key, this.item, this.callbackFunction}) : super(key: key);
#override
_ItemEditingPageState createState() => _ItemEditingPageState();
}
class _ItemEditingPageState extends State<ItemEditingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FlatButton(
child: Text('change name to aligator'),
onPressed: () {
///
/// if the name is [final], you create a new Item and pass it back
///
Item item = Item('aligator');
widget.callbackFunction(item);
///
/// if the name is not final you can just change it on the current object
///
//widget.item.name = 'aligator';
//widget.callbackFunction(widget.item);
},
),
),
),
);
}
}
edit: used a callback function instead of Navigator.pop() to notify the showcase page.