I am creating my custom widget in which I am using RaisedButton
I want to export onTap event from RaisedButton to parent widget. How can I do that?
Create a custom widget:
class MyCustomWidget extends StatelessWidget {
final String text;
final VoidCallback? onTap;
const MyCustomWidget(this.text, {
Key? key,
this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onTap,
child: Text(text),
);
}
}
Usage:
MyCustomWidget(
'My button',
onTap: () {},
)
Related
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.
I've created a Widget that's basically an ElevatedButton, except its width can be made proportional to the screen size (settable with WidthFactor).
import 'package:flutter/material.dart';
class FractionalButton extends StatelessWidget {
final double widthFactor;
final Widget child;
final Function onPressed;
FractionalButton({
required this.widthFactor,
required this.onPressed,
required this.child,
});
#override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: widthFactor,
child: ElevatedButton(
onPressed: onPressed(),
child: child,
),
);
}
}
Trouble is, it shows up gray instead of using the basic themed color of green.
ElevatedButton(
onPressed: () {},
child: const Text("Country"),
),
FractionalButton(
widthFactor: 1,
onPressed: () {},
child: const Text("Password"),
),
How can I get my Widget to use the current theme colors?
It's not an issue with the ElevatedButton but with the onPressed function you are passing to the custom ElevatedButton.
Instead of
final Function onPressed;
use
final VoidCallback onPressed;
or
final Function() onPressed;
and use it like below:
onPressed: onPressed,
Your FractionalButton will look like below.
class FractionalButton extends StatelessWidget {
final double widthFactor;
final Widget child;
final VoidCallback onPressed;
FractionalButton({
required this.widthFactor,
required this.onPressed,
required this.child,
});
#override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: widthFactor,
child: ElevatedButton(
onPressed: onPressed,
child: child,
),
);
}
}
EDIT
The reason it was happening was you were not passing on onPressed properly to the custom ElevatedButton and it was set to the disabled mode.
the problem is with te declaration of the function
try this :
final void Function() onPressed;
the whole code :
class FractionalButton extends StatelessWidget {
final double widthFactor;
final Widget child;
final void Function() onPressed;
FractionalButton({
required this.widthFactor,
required this.onPressed,
required this.child,
});
#override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: widthFactor,
child: ElevatedButton(
onPressed: onPressed,
child: child,
),
);
}
}
so when you use this widgets :
ElevatedButton(
onPressed: () {},
child: const Text("Country"),
),
FractionalButton(
widthFactor: 0.5,
onPressed: () { },
child: const Text("Password"),
),
this will be the result:
as you can see it use the same theme as the original ElevatedButton.
While learning Futter, I ran into a problem on how to execute two functions at the same time. One function which comes from a constructor in the Button Class together with another function that is inside the Button class.
This is a example code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
color: Colors.blueGrey,
child: Center(
child: Button(
whenButtonPressed: () {
print("Button pressed from outside the Button Class");
},
),
),
),
),
);
}
}
class Button extends StatelessWidget {
const Button({this.whenButtonPressed, Key? key}) : super(key: key);
final VoidCallback? whenButtonPressed;
#override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: whenButtonPressed,
//print("Button pressed from inside the Button Class"); <- Execute this additionally.
backgroundColor: Colors.red,
);
}
}
I have already tried to put the onPressed and the other print statement in curly brackets. But then only the print statement "Button pressed from inside the Button Class" is executed and not the whenButtonPressed function. What is the reason for this behaviour and how can I write this correctly?
class Button extends StatelessWidget {
const Button({this.whenButtonPressed, Key? key}) : super(key: key);
final VoidCallback? whenButtonPressed;
#override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: (){ whenButtonPressed; // <-now this is not executed.
print("Button pressed from inside the Button Class");},
backgroundColor: Colors.red,
);
}
}
As #Peter Koltai states in his solution you have to add () to whenButtonPressed -> whenButtonPressed().
The correct code to make this work is:
class Button extends StatelessWidget {
const Button({this.whenButtonPressed, Key? key}) : super(key: key);
final VoidCallback? whenButtonPressed;
#override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
whenButtonPressed!();
print("Button pressed from inside the Button Class");
},
backgroundColor: Colors.red,
);
}
}
I want to send data from widget to another widget, in my example I want to send onPressed value as a variable.
appBar: CancelAppBar(onPressed: ),
What should I write after onPressed (in the above code) to send the value:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UnderBuild()));
to a widget in a seperate dart file?
Following is the other file:
class CancelAppBar extends StatefulWidget implements PreferredSizeWidget {
CancelAppBar({Key? key, required this.onPressed}) : super(key: key);
final ValueGetter<String> onPressed;
static final _appBar = AppBar();
#override
Size get preferredSize => _appBar.preferredSize;
#override
_CancelAppBarState createState() => _CancelAppBarState();
}
class _CancelAppBarState extends State<CancelAppBar> {
get onPressed => null;
#override
Widget build(BuildContext context) {
return AppBar(
titleSpacing: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 8.w)),
IconButton(
onPressed: ,
icon: Icon(Icons.close),
)
],
),
backgroundColor: AppColors.dark,
);
}
}
You can access any StatefulWidget variable in the State class with widget getter, like:
Also, you can notice that:
Your onPressed variable has ValueGetter<String> type which is only a typedef to String Function()
The onPressed of IconButton widget has the type void Function()
Then you can use your variable like that:
class CancelAppBar extends StatefulWidget implements PreferredSizeWidget {
CancelAppBar({Key? key, required this.onPressed}) : super(key: key);
final ValueGetter<String> onPressed;
static final _appBar = AppBar();
#override
Size get preferredSize => _appBar.preferredSize;
#override
_CancelAppBarState createState() => _CancelAppBarState();
}
class _CancelAppBarState extends State<CancelAppBar> {
#override
Widget build(BuildContext context) {
return AppBar(
titleSpacing: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 8.w)),
IconButton(
onPressed: () {
/// Access your variable by [widget.onPressed]
widget.onPressed(); /// Call it inside this function because your variable doesn't match directly the [onPressed] of [IconButton] widget
},
icon: Icon(Icons.close),
)
],
),
backgroundColor: AppColors.dark,
);
}
}
Let's say I have the following custom widget:
MyWidget extends StatelessWidget{
Widget child;
......
const MyWidget(
{Key key,
this.child})
: super(key: key);
#override
Widget build(BuildContext context) {
//get child's size and use it in layout
}
}
How can I reach this? I know that I can do it using global keys but as I see this solution doesn't work here.
How would I do it if I didn't need to create separate StatelessWidget: I'd create GlobalKey and provided it in constructor, but here I can't provide it in constructor because I have already created child Widget in my custom widget.
You can set a GlobalKey for a widget wrapping the child widget, e.g., a Container or an IntrinsicHeight(which sets its height to its child's height) and get this widget's size using that key:
class MyWidget extends StatelessWidget {
final Widget child;
MyWidget({Key key, this.child}) : super(key: key);
final GlobalKey key1 = GlobalKey();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
IntrinsicHeight(child: child, key: key1),
FlatButton(
onPressed: () {
print(key1.currentContext.size);
},
child: Text('get height'),
)
],
);
}
}