I have a SelectableText Widget with a string which is a phone number
Starts with +
Has 12 digits
When the text is selected, the option to call it doesn't appear.
If I open the same text for example in a google search as below, I can see the option to call it. How can I make that in Flutter?
You may use the contextMenuBuilder property for this.
It will help you creating a different context menu depending on the current state of the user's selection:
More info: see contextMenuBuilder property in SelectableText widget doc
SelectableText(
'data to show',
contextMenuBuilder: (_, textState) => Row(
children: [
if (isPhoneNumber(textState.textEditingValue.text))
Container(), //Widget to make the phone call here
],
),
),
bool isPhoneNumber(String selection) {
if (!selection.startsWith('+')) return false;
return RegExp(r'^[0-9]+$').hasMatch(selection.substring(1));
}
I solved it by looking at the example pointed out by #Luis Utrera
Solution:
contextMenuBuilder: (context, EditableTextState editableTextState) {
return AdaptiveTextSelectionToolbar(
anchors: editableTextState.contextMenuAnchors,
children: [
Padding(
padding: const EdgeInsets.all(10),
child: IconButton(
icon: Icon(Icons.call),
onPressed: () {
// TODO: launch call app
},
),
),
...editableTextState.contextMenuButtonItems
.map((ContextMenuButtonItem buttonItem) {
return CupertinoButton(
borderRadius: null,
onPressed: buttonItem.onPressed,
padding: const EdgeInsets.all(10.0),
pressedOpacity: 0.7,
child: Text(
CupertinoTextSelectionToolbarButton.getButtonLabel(
context,
buttonItem,
),
),
);
})
.toList()
.cast(),
],
);
},
Related
This code is working, but I am not getting the output I want. I want to show this screen in a container but it's not working.
It's not opening in a container but instead directly opening as a different page.
I am using the flutter filter_list: ^1.0.2 plugin in this code.
I want to show this search bar with a list in a container on the same page.
List<SelectProjectData?> _selectProjectData = [];
openFilterDelegate() async {
await FilterListDelegate.show<SelectProjectData?>(
context: context,
list: _selectProjectData,
selectedListData: _selectedProjectData,
onItemSearch: (user, query) {
return user!.projectName!.toLowerCase().contains(query.toLowerCase());
},
tileLabel: (user) => user!.projectName,
emptySearchChild: const Center(child: Text('Data not found')),
// enableOnlySingleSelection: true,
searchFieldHint: 'Search Here..',
onApplyButtonClick: (list) {
setState(() {
_selectedProjectData = list!;
});
},
);
}
Try to call this in the column:- here you can find the code
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(20),
color: AppColor.WHITE,
child:
Column(
children: [
openFilterDelegate(),
],
),
);
The final result is this:-
Based on https://pub.dev/packages/filter_list, you want to use the FilterWidget instead of the FilterDelegate. FilterDelegate opens a new window every time. FilterWidget is a widget that you can put inside a container just like any other widget.
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(20),
color: AppColor.WHITE,
child:
Column(
children: [
FilterWidget(
<set up widget here>
),
],
),
);
I was building my Widgets from a list that was predefined in a file of MyClass I created. This worked but I wanted to be able to store persisted data for adding a Boolean favorite field.
I created the Hive Types/Fields for my class, generated the type adapters, and successfully loaded the Hive box on first run of the app, and I can print values to the console, so I know the data is all there and correct.
In the class I have, name, image url path to asset image and a favorite field.
Before when I was using the list to get my data I was able to get the image URL like this:
Expanded(child: Image.asset(widget.MyClass.imageURL)),
Now I want to get this from the Hive box
Box<MyClass> box = Hive.box<MyClass>('myClassBox');
//This is where I am stuck
Expanded(child: Image.asset(box.???)),
I tried box.values.where and box.get() to then get to imageURL field. But get requires a key, which I don't have to pass it from
Widget build(BuildContext context)
And I then have the same issue when trying to access the favorite field, which I am using the Favorite Button package (favorite_button 0.0.4). And I will then update the true/false value based on the button being tapped.
If someone can point me in the right direction that would be great.
Thanks.
Edit:
Here is the Widget:
Widget build(BuildContext context) => GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => TaskPage(job: widget.job), //Need to get data from Hive now
)),
child: Container(
padding: const EdgeInsets.all(16),
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Expanded(flex: 3, child: buildText()),
Expanded(child: Image.asset(widget.job.imageUrl)),//Need to get data from Hive now
GestureDetector(
child: Icon(
widget.job.fav ? Icons.favorite : Icons.favorite_border, //Need to get data from Hive now
),
onTap: () {
// add/remove from favorites list
}
),
],
),
),
);
Second Edit: Here is the same code after implementing the suggestion given
Widget build(BuildContext context) => GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => TaskPage(job: Hive.box<Job>('jobBox').get(context)), //This bit is still broken so I need to look at this
)),
child: Column(
children:
Hive.box<Job>('jobBox').values.toList().map(
(elementList) => Container(
padding: const EdgeInsets.all(16),
height: 100,
decoration: BoxDecoration(
color: white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Expanded(flex: 3, child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
elementList.name,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
),
SizedBox(height: 10),
//Text('Num tasks in job'),
],
)),
Expanded(child: Image.asset(elementList.imageURL)),
GestureDetector(
child: Icon(
elementList.fav
? Icons.favorite
: Icons.favorite_border,
color: elementList.fav ? Colors.red.shade200 : Colors.grey,
),
onTap: () {
//To do
}
// )
),
],
),
),
)
.toList(),
),
);
Assuming that you have only 1 data in the box, you can access that stored data like this.
Box<MyClass> box = Hive.box<MyClass>('myClassBox');
if(box.isNotEmpty) {
final data = box.values.first;
// use data
} else {
// empty state
}
Hive values could have keys, depending on how you use it. If you used box.put(key, value), you can use box.get(key) to work with keys and values.
If you used box.add(value), it stores the data with auto assigned indexes starting from 0. So you can usebox.getAt(index) to get a data with index.
In my app users are required to submit their government ID's for verification to keep using the app. On the basis of the condition "isIDverified" it displays a text "Verified" or if it's under review it displays "Under Review". Inside the verified condition I want to put a popup which will say "Your account is under review" along with the text somewhere around this green empty block.
My code:
if (isIDVerified) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Verified',
style: kAppBarTitleTextStyle.copyWith(color: primaryColor),
),
SizedBox(
width: _screenUtil.setWidth(10),
),
Icon(
Icons.verified_user,
size: kPreferredIconSize,
color: Colors.green,
),
],
);
} else if (isIDUnderReview) {
return
Text(
'ID Under Review',
style: kAppBarTitleTextStyle.copyWith(color: primaryColor),
);
As far as I understand your question, I would like to answer it.
For displaying popups, you can make use of AlertDialogs.
You can do something like this.
void informUser() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Under Review"),
content: Column(
children: [ LIST OF WIDGETS ]
),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
You can put your widgets in the Column widget of AlertDialog's content.
I'm using showMenu() to dispaly popup menu. Usually when you use PopupMenuButton it has the onSelected option, but you don't seem to have it with showMenu().
I tried wrapping the contents of PopupMenuItem inside GestureDetector, but that makes the clickable area just way too small. See picture below, the smaller rectangle is my GestureDetector (which works but is too small) and the bigger rectangle is the inkwell which comes with PopupMenuItem.
So my question is, how should I handle PopupMenuItem presses when I don't have the onSelected property?
EDIT:
Here is the code. I have ListTiles, which call this method on LongPress:
void _showOptionsMenu(int hiveIndex) {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
showMenu(
context: context,
position: RelativeRect.fromRect(
// magic code from stackoverflow, positions the PopupMenu on your tap location
_tapPosition & Size(40, 40),
Offset.zero & overlay.size,
),
items: [
PopupMenuItem(
value: 0,
child: Row(
children: [
Icon(Icons.edit),
Text("Edit"),
],
),
),
PopupMenuItem(
value: 1,
child: Row(
children: [
Icon(Icons.delete),
Text("Delete"),
],
),
),
],
);
You can't wrap the PopupMenuItems inside GestureDetector, since the items property only allows PopupMenuItems.
There is no need to wrap items into gesture detector.. show menu is an async method which returns value of the item menu. When you press any of the item you get back the value of that pressed item. With that value you can do whatever you want. Check this code
Future<void> _showOptionsMenu(int hiveIndex) async {
int selected = await showMenu(
position: RelativeRect.fromLTRB(60.0, 40.0, 100.0, 100.0),
context: context,
items: [
PopupMenuItem(
value: 0,
child: Row(
children: [
Icon(Icons.edit),
Text("Edit"),
],
),
),
PopupMenuItem(
value: 1,
child: Row(
children: [
Icon(Icons.delete),
Text("Delete"),
],
),
),
],
);
if (selected == 0) {
print('handle edit');
} else {
print('handle delete');
}
}
staff.ulogin is a list returned from a web service. If there is more than one item returned, I need to display of list of those items (displaying the company name). I can get the first item displaying, but I'm not sure how to display the entire list.
I also need the user to be able to tap an item so I can setup that company for use, so I'll need to know what item they chose. Thanks for any help.
if (staff.ulogin.length > 1) {
Alert(
context: context,
title: 'Choose Company',
content: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//how to display all the items
ListTile(
title: Text(staff.ulogin[0].company),
onTap () {}, // <--- how to get the index of the item tapped
),
],
),
),
buttons: [
DialogButton(
child: Text('Cancel', style: TextStyle(color: Colors.white, fontSize: 20)),
color: kMainColor,
onPressed: () {
Navigator.of(context).pop();
},
),
],
).show();
} else
I believe that the correct way of display a list o items is using a ListView. For this case you can use a ListView.builder like this:
Container(
height: 300.0, // Change as you wish
width: 300.0, // Change as you wish
child: ListView.builder
(
itemCount: staff.ulogin.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(staff.ulogin[index].company),
onTap () {
someFunction(staff.ulogin[index]);
},
),
}
)
)