Flutter Gallery demo: Cards to change card's height - flutter

I am trying to modify the cards_demo.dart found in Flutter examples. My purpose is that instead of having the built-in two cards' height be fixed as:
static final double height=300.0 (or some mandatory and fixed number), I want to have different height for the two cards.
So I modified the TravelDestination class to include a property height:
class TravelDestination {
const TravelDestination({ this.assetName, this.title, this.description, this.height });
final String assetName;
final String title;
final List<String> description;
final double height;
bool get isValid => assetName != null && title != null && description?.length == 3;
}
Then, in class TravelDestinationItem build function:
class TravelDestinationItem extends StatelessWidget {
TravelDestinationItem({ Key key, #required this.destination }) : super(key: key) {
assert(destination != null && destination.isValid);
}
static final double height = 512.0;
final TravelDestination destination;
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final TextStyle titleStyle = theme.textTheme.headline.copyWith(color: Colors.white);
final TextStyle descriptionStyle = theme.textTheme.subhead;
return new Container(
padding: const EdgeInsets.all(8.0),
height: destination.height,
//height: height,
child: new Card(
child: new Column(... ...
I assigned different height property to the two cards but the result is not working: they are still the same height as specified by static final double height.
If I comment out static final double height line, the compiler will remind me: No static getter 'height' declared...
I am very much confused on this behavior.
Can anyone help?

Since you're using items of varying height, you should remove this line from the call to the ListView constructor:
itemExtent: TravelDestinationItem.height,
Also, you'll need to hot-restart the app (hot-reloading won't update the destinations list with the new data, since it's a global variable).

Related

Flutter: create const color from hex string

I am using Firebase remote config to store my color values. This gives me the flexibilty to update colors without the need to update my app. Now I have written myself a helper function which returns the color object.
In my Firebase remote config I have stored the hex color codes as strings. However, now I am facing the problem that my colors are no constants (const). This is a huge problem for me as I have set default color values in some constructors like here:
const CustomIcon(
{required this.iconType,
this.size,
this.color = Helper.getColor("black"),
Key? key})
: super(key: key);
Because my color is not a const value anymore I get the following error: https://dart.dev/tools/diagnostic-messages#non_constant_default_value
This is my helper function:
static Color getColor(colorName) {
final remoteConfig = FirebaseRemoteConfig.instance;
String colorString = remoteConfig.getString(colorName);
const color = Color(int.parse(colorString));
return color;
}
Do you have any idea on how I can solve this problem?
Kind regards
You sadly won't be able to const anything from the API. The const keyword implies that the Dart Analyzer knows what the value will be even before compiling. This isn't the case here, as the values come from the API.
However, you can still have a solution, by using a local Color default value, and checking for a null color.
class CustomIcon extends StatelessWidget {
final String iconType;
final int? size;
final Color? color;
late final Color defaultColor = Helper.getColor("black");
CustomIcon({required this.iconType, this.size, this.color, Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
final _color = color ?? defaultColor;
// Build your Widget
return Container(
color: _color,
height: 50,
width: 50,
);
}
}
Here is a simple DartPad showing it in action: https://dartpad.dev/?id=562c943972abaefd29b7b264e16ad5aa

Flutter: how to build a custom toolbar for Selectable Text~

I'm building a ChatApp, and I need a feature that can allow user to select text from the widget, and also show some of my custom actions, such as Deleting that message, Share etc.
And I found that the SelectableText widget would be helpful, but the only downside of it is that I have to create a custom TextSelectionControls class and pass it to the SelectableText widget selectionControls property in order to add my own actions, but I really don't know how to do it, so does anyone have idea about how to create this kind of class?
I already visited this link: Github: Custom text selection menu example, but after I copy and paste his code, it shows me this error message at compile time:
Missing concrete implementations of 'TextSelectionControls.buildHandle', 'TextSelectionControls.getHandleAnchor', and 'TextSelectionControls.getHandleSize'.
Try implementing the missing methods, or make the class abstract.dart(non_abstract_class_inherits_abstract_member)
But I don't think I missed something, since the people on Github doesn't, so is it because the issue of the Flutter framework itself?
The code from the link you've provided works. Are you sure you are using
class MyMaterialTextSelectionControls extends MaterialTextSelectionControls
Here is refactored working example
class MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
// Padding between the toolbar and the anchor.
static const double _kToolbarContentDistanceBelow = 10.0;
static const double _kToolbarContentDistance = 8.0;
/// Builder for material-style copy/paste text selection toolbar.
#override
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset selectionMidpoint,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
ClipboardStatusNotifier clipboardStatus,
Offset? lastSecondaryTapDownPosition,
) {
final TextSelectionPoint startTextSelectionPoint = endpoints[0];
final TextSelectionPoint endTextSelectionPoint =
endpoints.length > 1 ? endpoints[1] : endpoints[0];
final Offset anchorAbove = Offset(
globalEditableRegion.left + selectionMidpoint.dx,
globalEditableRegion.top +
startTextSelectionPoint.point.dy -
textLineHeight -
_kToolbarContentDistance,
);
final Offset anchorBelow = Offset(
globalEditableRegion.left + selectionMidpoint.dx,
globalEditableRegion.top +
endTextSelectionPoint.point.dy +
_kToolbarContentDistanceBelow,
);
final value = delegate.textEditingValue;
return MyTextSelectionToolbar(
anchorAbove: anchorAbove,
anchorBelow: anchorBelow,
clipboardStatus: clipboardStatus,
handleCustomButton: () {
print(value.selection.textInside(value.text));
delegate.hideToolbar();
},
);
}
}
class MyTextSelectionToolbar extends StatelessWidget {
const MyTextSelectionToolbar({
Key? key,
required this.anchorAbove,
required this.anchorBelow,
required this.clipboardStatus,
required this.handleCustomButton,
}) : super(key: key);
final Offset anchorAbove;
final Offset anchorBelow;
final ClipboardStatusNotifier clipboardStatus;
final VoidCallback? handleCustomButton;
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final List<_TextSelectionToolbarItemData> items =
<_TextSelectionToolbarItemData>[
_TextSelectionToolbarItemData(
onPressed: handleCustomButton ?? () {},
label: 'Custom button',
),
];
int childIndex = 0;
return TextSelectionToolbar(
anchorAbove: anchorAbove,
anchorBelow: anchorBelow,
toolbarBuilder: (BuildContext context, Widget child) =>
Container(color: Colors.pink, child: child),
children: items
.map((_TextSelectionToolbarItemData itemData) =>
TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(
childIndex++, items.length),
onPressed: itemData.onPressed,
child: Text(itemData.label),
))
.toList(),
);
}
}
class _TextSelectionToolbarItemData {
const _TextSelectionToolbarItemData({
required this.label,
required this.onPressed,
});
final String label;
final VoidCallback onPressed;
}
anyway you can check this package text_selection_controls

Flutter: add a simple wrapper to the Text widget

I came from a React world and trying to get my head around Flutter and Dart.
I'm using the Text widget with the same parameters a lot, so it seems reasonable to think of a way to reuse code. I created a wrapper that uses it:
import 'package:flutter/material.dart';
TextStyle getThemeProperty(type, TextTheme textTheme) {
switch (type) {
case 'headline1':
return textTheme.headline1;
case 'headline2':
return textTheme.headline2;
case 'headline3':
return textTheme.headline3;
default:
return textTheme.bodyText2;
}
}
class CustomText extends StatelessWidget {
const CustomText({Key key, this.type, this.text, this.color}) : super(key: key);
final type;
final text;
final color;
#override
Widget build(BuildContext context) {
var textTheme = Theme.of(context).textTheme;
var style = getThemeProperty(type, textTheme);
if (this.color != null) style.color = this.color;
return Text(
this.text,
style: style,
);
}
}
// Usage
CustomText(
text: 'Some Heading',
type: 'headline2',
color: Colors.black
)
The idea is to set the color if the color property is passed as a parameter, but Dart's compiler doesn't like it. It throws me the error: ''color' can't be used as a setter because it's final.
Try finding a different setter, or making 'color' non-final.'
I'm planning to do the same to fontWeight and textAlign properties as well. How am I able to make this work, I mean, to add new props to the style object on demand?
The reason why the dart compiler is unhappy is just because the color property of the TextStyle is declared as final. Therefore to use a new color, you have to create a new instance of the TextStyle.
Luckily, the TextStyle class comes with a copyWith method that returns an edited copy of your TextStyle
final type;
final text;
final color;
#override
Widget build(BuildContext context) {
var textTheme = Theme.of(context).textTheme;
var style = getThemeProperty(type, textTheme);
return Text(
this.text,
// Added this...
style: style.copyWith(color: color ?? style.color),
);
}
As a side note, when making reusable widgets, it's always a good idea to type your parameters. This is because any type of variable can be used. So instead of passing a String for text, you may pass an int
// DON'T DO THIS
final type;
final text;
final color;
// DO THIS
final String type;
final String text;
final Color color;
Also adding the this keyword to reference a variable in a class without variable shadowing is unnecessary.
// DON'T
this.text
// DO
text

Flutter: Differentiate between Widget param = null and an unset param

I have a CustomButton like so:
class CustomButton extends StatelessWidget {
const CustomButton(
{Key key, this.onPressed, this.child, this.padding})
: super(key: key);
final VoidCallback onPressed;
final Widget child;
final EdgeInsetsGeometry padding;
#override
Widget build(BuildContext context) {
final defaultPadding = EdgeInsets.symmetric(vertical: 13);
return FlatButton(
padding: padding ?? defaultPadding,
child: child,
onPressed: onPressed,
// + some irrelevant custom properties
);
}
}
I've included various applications of the widget, along with the desired outcome:
CustomButton(child: Text('👋'), onPressed: () {}) // 1⃣ no padding specified; use default
CustomButton(..., padding: null) // 2⃣ use NO padding
CustomButton(..., padding: EdgeInsets.only(left:1)) // 3⃣ use specified padding
The issue is that I can't differentiate between when padding is not passed 1⃣ and when padding: null is passed 2⃣ . In both cases, defaultPadding is applied -- whereas I want padding: null to result in no padding being applied.
How can I do this?
In your case, why not just let the default value for padding be your desired default? EdgeInsets.symmetric is a const constructor, so it can be used to create a default argument:
class CustomButton extends StatelessWidget {
static const defaultPadding = EdgeInsets.symmetric(vertical: 13);
const CustomButton(
{Key key, this.onPressed, this.child, this.padding = defaultPadding})
: super(key: key);
Normally you should use default arguments when you can. You'd use ?? for defaults when you can't (because the default you want isn't const) or when you do want null and an omitted argument to be handled in the same way.
In general, there is no built-way to distinguish between a default value being explicitly passed and a default value being used because it was omitted. In some cases you maybe could create a sentinel value that would be impossible for callers outside your library to pass:
class Foo {
const Foo(this.x);
final int x;
}
// Private so that nothing outside the library can pass it.
const _defaultFoo = Foo(0);
void bar({Foo foo = _defaultFoo}) {
if (foo == _defaultFoo) {
// Argument omitted.
} else {
// Argument was explicitly supplied.
}
}
final EdgeInsetsGeometry padding;
The padding doesn't have any initial value, thus whether you pass it null or nothing it will be null anyway.
you could fix this by changing your class constructor to this :
const CustomButton({Key key, this.onPressed, this.child, this.padding = EdgeInsets.zero})
this way the padding will have zero value instead of null if it is not given any value when it's being instantiated.
This will do what you want but vice versa, when it's passed null it will get default padding, if not passed anything, zero padding.
Hope it helps.
See the following example
class A {
final int a;
const A({this.a = 10});
}
void callMe({A par1 = const A(a: 10)}) {
if(par1 != null)
{
print(par1.a);
}
else {
print("null");
}
}
void main() {
callMe(par1: null);
callMe();
callMe(par1: A(a: 100));
}
So in your case you can do this simply tweak your constructor.
const CustomButton(
{Key key, this.onPressed, this.child, this.padding = EdgeInsets.symmetric(vertical: 13)})
: super(key: key);

How to set icon size for childs on parent widget

I'm creating a widget where I want to set the the size of the icons used in the sub-widgets globally.
class ItemsContainer extends StatefulWidget {
final List<Item> items;
final double iconSize; //global default
const ItemsContainer({
#required this.items,
this.iconSize = 56.0,
});
}
class Item {
final Icon icon;
const Item ({
#required this.icon,
});
}
What I'd like to do is this:
for (var item in items) {
if (item.size == null)
item.size = iconSize;
}
The problem I face is, that I can't set the size due to the fact, that I have a const constructor.
I could clone an existing Icon and change the original size, but is there a better way to do it?
Icon _getSizedIcon(Icon icon, double size) {
return icon.size != null ? icon :
Icon(icon.icon,
size: size,
color: icon.color,
key: icon.key,
semanticLabel: icon.semanticLabel,
textDirection: icon.textDirection,
);
}
The IconTheme widget is what you probably want:
https://api.flutter.dev/flutter/widgets/IconTheme-class.html