How to increase the width of a PopupMenuEntry in flutter - flutter

I created a horizontal popup menu but i cant increase the width of the popupMenuEntry because the default width is too short for my use case. I tried to set the child of my popupMenuEntry to double.infinity and MediaQuery.of(context).sized.width but nothing is happening..
It looks like ok in the emulator but when i tested out in the actual device, its too small thats why i need to resize it to at least 90-95% of the screen width.
here is my implementation of my popupMenu.
class ReactionPopupMenu extends StatefulWidget {
const ReactionPopupMenu(
{Key? key, required this.onSelect, required this.child, this.onTap})
: super(key: key);
final void Function(String) onSelect;
final Widget child;
final VoidCallback? onTap;
#override
State<ReactionPopupMenu> createState() => _ReactionPopupMenuState();
}
class _ReactionPopupMenuState extends State<ReactionPopupMenu>
with CustomPopupMenu {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
onTapDown: storePosition,
onLongPress: () => this.showMenu(
context: context,
shape: const StadiumBorder(),
items: [
CustomedPopupEntry(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
for (var i = 0; i < kEmojies.length; i++)
ReactiveEmoji(
emoji: kEmojies[i],
onTap: () =>
Navigator.pop(context, kValue[i]))
]),
),
)
]).then(
(value) => value == null ? null : widget.onSelect(value)),
child: widget.child);
}
}
mixin CustomPopupMenu<T extends StatefulWidget> on State<T> {
Offset? _tapPosition;
/// Pass this method to an onTapDown parameter to record the tap position.
void storePosition(TapDownDetails details) =>
_tapPosition = details.globalPosition;
/// Use this method to show the menu.
// ignore: avoid_shadowing_type_parameters
Future<T?> showMenu<T>({
required BuildContext context,
required List<PopupMenuEntry<T>> items,
T? initialValue,
double? elevation,
String? semanticLabel,
ShapeBorder? shape,
Color? color,
bool captureInheritedThemes = true,
bool useRootNavigator = false,
}) {
// final RenderObject? overlay =
// Overlay.of(context)!.context.findRenderObject();
return material.showMenu<T>(
context: context,
position: RelativeRect.fromLTRB(
_tapPosition!.dx,
_tapPosition!.dy,
_tapPosition!.dx,
_tapPosition!.dy,
),
items: items,
initialValue: initialValue,
elevation: elevation,
semanticLabel: semanticLabel,
shape: shape,
color: color,
useRootNavigator: useRootNavigator,
);
}
}
class CustomedPopupEntry<T> extends PopupMenuEntry<T> {
const CustomedPopupEntry({Key? key, required this.child}) : super(key: key);
final Widget child;
#override
State<StatefulWidget> createState() => _CustomedPopupEntryState();
#override
double get height => 100;
#override
bool represents(T? value) => false;
}
class _CustomedPopupEntryState extends State<CustomedPopupEntry> {
#override
Widget build(BuildContext context) => widget.child;
}
class ReactiveEmoji extends StatelessWidget {
const ReactiveEmoji({Key? key, required this.emoji, required this.onTap})
: super(key: key);
final String emoji;
final VoidCallback onTap;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: MediaQuery.of(context).size.width * 0.046,
backgroundImage: AssetImage(emoji),
),
);
}
}

Your solution is not with adding more width to popUpMenu. you should make your emoji list scrollable, so when use it in small device it could be scrolled. So wrap your emoji row with SingleChildScrollView and set its scrollDirection to horizontal.

Related

Flutter How to get size of dynamic widget

What I've done:
The black rectangle is the size of the canvas.
const double radius = 50;
class TableShape extends StatelessWidget {
final String name;
final Color color;
const TableShape({
Key? key,
required this.name,
required this.color,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap:(){debugPrint("ok");},
child: LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth;
final textPainter = TextPainter(
text: TextSpan(
text: name,
style: const TextStyle(fontFamily: 'Graphik', fontSize: 30, color: Colors.white),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center
);
textPainter.layout(maxWidth: maxWidth);
return CustomPaint(
size: Size(textPainter.width>radius*2?textPainter.width:radius*2, radius*2),
painter: MyPainter(color: color, txt: textPainter),
);
})
);
}
}
class MyPainter extends CustomPainter {
TextPainter txt;
Color color;
MyPainter({
required this.txt,
required this.color,
});
#override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
var paint = Paint()..color = color;
bool txtLarger = txt.width>radius*2;
canvas.drawCircle(Offset(txtLarger?txt.width/2:radius,radius), radius, paint);
//table name:
txt.paint(canvas, Offset(txtLarger?0:radius-txt.width/2,radius-txt.height/2));
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
#override
bool hitTest(Offset position) {
return sqrt(pow(txt.width/2-position.dx,2)+pow(radius-position.dy,2)) <= radius;
}
}
I need to get the width because I place the widget on my screen according to its width. The width is dynamic: the bigger the text, the wider the canvas. Is it possible ? Or maybe you have an other approach to get this widget than the way I did ?
get widget size by global key:
final GlobalKey _widgetKey = GlobalKey();
Size _getSize(GlobalKey key){
final State state = key.currentState;
final BuildContext context = key.currentContext;
final RenderBox box = state.context.findRenderObject();
return context.size;
}
Widget build(BuildContext context) {
return GestureDetector(
key: _widgetKey,
onTap:(){_getSize(_widgetKey);},
child: LayoutBuilder(
builder: (context, constraints) {
Use GlobalKey to find RenderBox then get the size. Remember you need to make sure the widget was rendered.
Example:
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) => const MaterialApp(home: Home());
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
var key = GlobalKey();
Size? redboxSize;
#override
void initState() {
WidgetsBinding.instance?.addPostFrameCallback((_) {
setState(() {
redboxSize = getRedBoxSize(key.currentContext!);
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Example')),
body: Column(
children: [
SizedBox(
height: 100,
child: Center(
child: Container(
key: key,
child: const Text('Hello oooooooooooooooo'),
color: Colors.redAccent,
),
),
),
if (redboxSize != null) Text('Redbox size: $redboxSize')
],
),
);
}
Size getRedBoxSize(BuildContext context) {
final box = context.findRenderObject() as RenderBox;
return box.size;
}
}

where does FocusScope widget exist in the widget tree?

Where does the FocusScope widget create in the tree and we pass every context in it and it can request to any focus nodes. When we pass context to FocusScope it will start looking above the context and we never used the FocusScope widget in the code in the hierarchy where does it create and how does it resolves in the case of scaffold if we pass context that is above in the tree then it throws an exception then we use builder type of thing but in FocusScope why it doesn't throw an error?
Here is the example for the FocusScope
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// A demonstration pane.
///
/// This is just a separate widget to simplify the example.
class Pane extends StatelessWidget {
const Pane({
Key? key,
required this.focusNode,
this.onPressed,
required this.backgroundColor,
required this.icon,
this.child,
}) : super(key: key);
final FocusNode focusNode;
final VoidCallback? onPressed;
final Color backgroundColor;
final Widget icon;
final Widget? child;
#override
Widget build(BuildContext context) {
return Material(
color: backgroundColor,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: child,
),
Align(
alignment: Alignment.topLeft,
child: IconButton(
autofocus: true,
focusNode: focusNode,
onPressed: onPressed,
icon: icon,
),
),
],
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool backdropIsVisible = false;
FocusNode backdropNode = FocusNode(debugLabel: 'Close Backdrop Button');
FocusNode foregroundNode = FocusNode(debugLabel: 'Option Button');
#override
void dispose() {
super.dispose();
backdropNode.dispose();
foregroundNode.dispose();
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
final Size stackSize = constraints.biggest;
return Stack(
fit: StackFit.expand,
// The backdrop is behind the front widget in the Stack, but the widgets
// would still be active and traversable without the FocusScope.
children: <Widget>[
// TRY THIS: Try removing this FocusScope entirely to see how it affects
// the behavior. Without this FocusScope, the "ANOTHER BUTTON TO FOCUS"
// button, and the IconButton in the backdrop Pane would be focusable
// even when the backdrop wasn't visible.
FocusScope(
// TRY THIS: Try commenting out this line. Notice that the focus
// starts on the backdrop and is stuck there? It seems like the app is
// non-responsive, but it actually isn't. This line makes sure that
// this focus scope and its children can't be focused when they're not
// visible. It might help to make the background color of the
// foreground pane semi-transparent to see it clearly.
canRequestFocus: backdropIsVisible,
child: Pane(
icon: const Icon(Icons.close),
focusNode: backdropNode,
backgroundColor: Colors.lightBlue,
onPressed: () => setState(() => backdropIsVisible = false),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// This button would be not visible, but still focusable from
// the foreground pane without the FocusScope.
ElevatedButton(
onPressed: () => debugPrint('You pressed the other button!'),
child: const Text('ANOTHER BUTTON TO FOCUS'),
),
DefaultTextStyle(
style: Theme.of(context).textTheme.headline2!,
child: const Text('BACKDROP')),
],
),
),
),
AnimatedPositioned(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 300),
top: backdropIsVisible ? stackSize.height * 0.9 : 0.0,
width: stackSize.width,
height: stackSize.height,
onEnd: () {
if (backdropIsVisible) {
backdropNode.requestFocus();
} else {
foregroundNode.requestFocus();
}
},
child: Pane(
icon: const Icon(Icons.menu),
focusNode: foregroundNode,
// TRY THIS: Try changing this to Colors.green.withOpacity(0.8) to see for
// yourself that the hidden components do/don't get focus.
backgroundColor: Colors.green,
onPressed: backdropIsVisible
? null
: () => setState(() => backdropIsVisible = true),
child: DefaultTextStyle(
style: Theme.of(context).textTheme.headline2!,
child: const Text('FOREGROUND')),
),
),
],
);
}
#override
Widget build(BuildContext context) {
// Use a LayoutBuilder so that we can base the size of the stack on the size
// of its parent.
return LayoutBuilder(builder: _buildStack);
}
}

Flutter - How to Extract Widget with onPressed setState inside?

I want to Extract a Widget with onPressed setState inside but I get the Message "Reference to an enclosing class method cannot be extracted."
Is there a way to do that?
I would like to divide my code into different widgets so that it remains clear. Here is simplified an example of the code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Calculator(),
);
}
}
class Calculator extends StatefulWidget {
#override
_CalculatorState createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
var myValue = 0;
void calculate() {
myValue = 12;
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: TextButton(
onPressed: () {
setState(() {
calculate();
});
},
child: Text(
'Button 001',
),
),
),
TextOutput(myValue: myValue),
],
),
);
}
}
class TextOutput extends StatelessWidget {
const TextOutput({
Key key,
#required this.myValue,
}) : super(key: key);
final int myValue;
#override
Widget build(BuildContext context) {
return Container(
child: Text(
myValue.toString(),
),
);
}
}
The part I want to extract into a separate widget:
Container(
child: TextButton(
onPressed: () {
setState(() {
calculate();
});
},
child: Text(
'Button 001',
),
),
),
Flutter offers VoidCallback and Function(x) (where x can be a different type) for callback-style events between child and parent widgets.
Simply You can pass Function onPressed; via constructor
Here is your Extracted Container widget:
class ExtractedContainer extends StatelessWidget {
final Function onPressed;
const ExtractedContainer({
Key key, #required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: () {
onPressed();
},
child: Text(
'Button 001',
),
),
);
}
}
And Here How to use it:
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ExtractedContainer(onPressed: calculate,),
TextOutput(myValue: myValue),
],
),
);
}
Your full code example
import 'package:flutter/material.dart';
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Calculator(),
);
}
}
class Calculator extends StatefulWidget {
#override
_CalculatorState createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
var myValue = 0;
void calculate() {
myValue = 12;
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ExtractedContainer(onPressed: calculate,),
TextOutput(myValue: myValue),
],
),
);
}
}
class ExtractedContainer extends StatelessWidget {
final Function onPressed;
const ExtractedContainer({
Key key, #required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: () {
onPressed();
},
child: Text(
'Button 001',
),
),
);
}
}
class TextOutput extends StatelessWidget {
const TextOutput({
Key key,
#required this.myValue,
}) : super(key: key);
final int myValue;
#override
Widget build(BuildContext context) {
return Container(
child: Text(
myValue.toString(),
),
);
}
}
Setstate is related to the widget you want to refresh its state. If you extract it to another place, then setState refers to the state of the new widget.
In your case, the setState will only change the state of the container encapsulating your widget which you are trying to extract and its children, it doesn't migrate upward.
Unless, you look for the state of the widget you want, using exact type, and then trigger the state there, but this is overkill, a lot harder, requires more code, than what you currently have.
You can use VoidCallback on extract widget to get onPressed event
class MyContainer extends StatelessWidget {
final VoidCallback onTap;
const MyContainer({
Key? key,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: onTap,
child: Text(
'Button 001',
),
),
);
}
}
And use like
MyContainer(
onTap: () {
print("tapped");
setState(() {
calculate();
});
},
),

How to change CheckboxListTile height?

Shortly, I need the selectable area to be smaller in terms of height.
Text and Checkbox sizes are good, but the surrounding box is too big for the checklist I'm trying to create.
CheckBoxListTile current height vs. desired height
Tried wrapping it with Transform.scale but text becomes too small:
Transform.scale(
scale: 0.8,
child: CheckboxListTile(
title: const Text("Rinite alérgica"),
value: timeDilation != 1.0,
controlAffinity: ListTileControlAffinity.leading,
onChanged: (bool value) {
setState(() {
timeDilation = value ? 2.0 : 1.0;
});
},
),
),
Tried wrapping it with Container, but I get on-screen overflow warnings.
Does anyone have a better solution?
When trying to resize the CheckboxListFile it seems that Google actually recommends creating a custom Tile widget and making use of the Checkbox widget.
https://api.flutter.dev/flutter/material/CheckboxListTile-class.html#material.CheckboxListTile.3
Look at the LabeledCheckbox widget that's been created. You can very easily modify all components to fit your needs. For example, if you wished to make the Widget itself smaller, you can now wrap it in a Container
/// Flutter code sample for CheckboxListTile
// ![Custom checkbox list tile sample](https://flutter.github.io/assets-for-api-docs/assets/material/checkbox_list_tile_custom.png)
//
// Here is an example of a custom LabeledCheckbox widget, but you can easily
// make your own configurable widget.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
class LabeledCheckbox extends StatelessWidget {
const LabeledCheckbox({
Key key,
#required this.label,
#required this.padding,
#required this.value,
#required this.onChanged,
}) : super(key: key);
final String label;
final EdgeInsets padding;
final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
onChanged(!value);
},
child: Container(
padding: padding,
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
Checkbox(
value: value,
onChanged: (bool newValue) {
onChanged(newValue);
},
),
],
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _isSelected = false;
#override
Widget build(BuildContext context) {
return LabeledCheckbox(
label: 'This is the label text',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
);
}
}
Have you tried ?
SizedBox(
height: 50,
width: 50,
child: ......
)

Handling children taps

I'm beginner on Flutter and I'm trying to create a custom Widget called IconSelect. It should render a list of icons with a legend and the user will choose only one option. When the user taps an icon, it should change the background color of the selected icon and deselect all others.
My first aproach was to create an IconSelect class as a Stateful widget, and another widget called IconSelectItem as Stateless. And the IconSelect would have a children property, containing instances of IconSelectItem.
How can I handle the children taps to change the IconSelect state? Any ideas of others aproaches?
My code:
class IconSelect extends StatefulWidget {
final List<IconSelectItem> children;
final ValueChanged<int> onSaved;
IconSelect({
this.children,
this.onSaved
});
#override
State<StatefulWidget> createState() => new IconSelectState();
}
class IconSelectState extends State<IconSelect> {
int _selectedValue;
_handleTap(int value) {
setState(() {
_selectedValue = value;
});
widget.onSaved(_selectedValue);
}
#override
Widget build(BuildContext context) {
return new Row(
children: widget.children,
);
}
#override
void initState() {
super.initState();
// I tried the code below without success
widget.children.forEach((IconSelectItem item) {
item.onTap = _handleTap(item);
});
}
}
class IconSelectItem extends StatelessWidget {
final Icon icon;
final String legend;
final int value;
VoidCallback onTap;
final bool _selected = false;
IconSelectItem({
Key key,
this.icon,
this.legend,
this.value,
}) : super(key: key);
_handleTap() {
onTap();
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () => _handleTap(),
child: new Column(
children: <Widget>[
new CircleAvatar(
radius: 30.0,
child: icon,
backgroundColor: _selected ? Colors.blue : Colors.white,
),
new Center(
child: new Text(legend),
)
],
),
);
}
}
call setState on IconSelectItem's ancestor:
class YourPageState extends State<YourPage> {
int _selectedValue;
#override
Widget build(BuildContext context) {
return new Row(
children: widget.items.map((Item item) {
return new GestureDetector(
onTap: () {
// this class is a ancestor of IconSelectItem.
// setState will rebuild children.
setState(() {
_selectedValue = value;
});
},
child: new IconSelectItem(
icon: item.icon,
legend: item.legend,
value: item.value,
// every time _selectedValue changes,
// IconSelectItem is rebuild by setState.
selected: item.value == _selectedValue,
),
);
}).toList(),
);
}
}
class IconSelectItem extends StatelessWidget {
final Icon icon;
final String legend;
final int value;
final bool selected;
IconSelectItem({
Key key,
this.icon,
this.legend,
this.value,
this.selected = false,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new CircleAvatar(
radius: 30.0,
child: icon,
backgroundColor: selected ? Colors.blue : Colors.white,
),
new Center(
child: new Text(legend),
),
],
);
}
}