how to add Circular shape to a DropdownButton in flutter? - dart

I want to know how to customize DropdownButton by adding circular shape border like below in the picture .

It's not possible to do that in a simple way. You have to edit the classes _DropdownMenuPainter and _DropdownMenuState from dropdrown.dart, which is part of the Flutter's material package.
In _DropdownMenuPainter, change the BoxDecoration radius set in constructor:
_DropdownMenuPainter({
...
this.resize,
}) : _painter = BoxDecoration(
...
borderRadius: BorderRadius.circular(16.0), // Set the radius you want here
boxShadow: kElevationToShadow[elevation],
).createBoxPainter(),
super(repaint: resize);
In _DropdownMenuState, inside the method build(), wrap the Material widget with a ClipRRect and set its borderRadius the same as you set for _DropdownMenuPainter:
return FadeTransition(
opacity: _fadeOpacity,
child: CustomPaint(
...
child: Semantics(
...
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0), // This must be identical to the one set before
child: Material(
...
),
),
),
),
);
Notice that you'll lose those changes when you update Flutter.
Update:
I was going to make a PR to Flutter, but spent 1 hour trying to run the tests and had no success, so I'm not going to waste my time on this. Instead, I made a gist, so you can copy the whole file with the changes I made to your local Flutter copy.
https://gist.github.com/hugocbpassos/2a63594a21d7f231e97dd7dc1f18ee68
To use it, just set the radius property:
DropdownButton(
radius: 16,
items: [
...
],
);

Related

Laying out widgets from the corners and grow one with window

I’d like to place
an unspecified number of Text widgets from top-left to bottom
3 TextButton widgets from bottom-right to left and
a random parent content widget top-right
The Text and TextButton widgets should only take their minimum space while the the content widget should expand to bottom-left (upon windows resizing) with its children centered like so.
Maybe I’m just unlucky or misled/spoiled by Qt’s QSpacerItem…?
This is a desktop App so “windows resizing” really makes sense in flutter context! ;)
Also, more generally, is there a way to directly nest (some kind of) Row/Column instances and stretch Widgets down/right?
You'll want to use a combination of Row, Column and Expanded to achieve this effect.
I'm going to assume that the bottom left corner belongs to the left-hand side bar. With that in mind, your basic structure can look something like this:
Row(
children: [
// Side bar
SizedBox(
width: 250,
ListView.builder(
// Display text widgets
),
),
// This widget will fill whatever space remains in the Row after the side bar
Expanded(
child: Column(
children: [
// This widget will fill all the space before the footer
const Expanded(
child: Center(
child: Text("Resizable Window"),
),
),
// Footer with buttons
SizedBox(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// Your buttons
]
),
),
],
),
),
],
)
Full example on DartPad

Is there a more optimal way to space widgets within a column?

So i'm emulating an old app I have where the UI looks like this
Currently i'm focused on creating the UI layout for the purple side bar, I did that by creating a Container with a purple background. Within the container I created a column with multiple children and just used an empty SizedBox to create distance between one widget from another.
import 'package:flutter/material.dart';
class SignInPage extends StatelessWidget {
const SignInPage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text('Sample Text'),
// elevation: 5.0,
// ),
body: _buildContent(),
);
}
Widget _buildContent() {
// private method
return Container(
color: Colors.deepPurpleAccent,
padding: EdgeInsets.all(16.0),
child: Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: Colors.orange,
child: SizedBox(
height: 100.0,
width: 80.0,
),
),
SizedBox(
height: 140.0,
),
Container(
color: Colors.red,
child: SizedBox(
height: 50.0,
width: 80.0,
),
),
SizedBox(
height: 8.0,
),
Container(
color: Colors.purple,
child: SizedBox(
height: 50.0,
width: 80.0,
),
),
],
),
);
}
}
I'm quite new to flutter so i'm wondering if there's a better way to structure my layout of the side bar? Also to think in the future, since the name "BagTrack" Is on the same level as "Analytic Overview" Should that just be one giant row"?
Actual answer
Method 1
#Mahfuzur Rahman answer is good, but to actually answer your question about other ways. Flex widgets (Column and Row extend the Flex widget), have both mainAxisAlignment and crossAxisAlignment, they can be used to align them more easily between different devices/screens sizes.
So maybe grouping your red and purple boxes in a Column with mainAxisSize set to MainAxisSize.min, and aligning the surrounding column with a specific size.
https://api.flutter.dev/flutter/widgets/Column-class.html
Method 2
Another way, if you would like some widget to occupy some percentage amount of space from it's parent, I suggest you look into Expanded widget, Flexible widget.
Method 3
Or even FractionallySizedBox could be a good widget for you, but then I would also look at this LayoutBuilder widget to solve the potential Unbounded Height/Width exception.
Second smaller question
It's entirely up to you to decide about your second question concerning the giant row. I wouldn't do it though. Probably would use a const SizedBox or const EdgeInsets (for Padding) and keep them at the same height this way.
Just complementing Flutter knowledge
PS: Since you are new to Flutter. As a suggestion for future performance: avoid the Container widget as much as you know, there are a lot of simpler widgets like SizedBox, ColoredBox, DecoratedBox and Padding that you can use in its place that could be marked as const sometimes and be less expensive.
For understanding final and const:
final is a variable that cannot be reassigned by accident inside your code. When you instantiate it you give it a value and that's it. (Using late changes that a bit but not much);
const is a variable assigned by the compiler, if you are familiar with C it's like #define but there is a little difference, every time you say const EdgeInsets.all(8) for example, the compiler will detect that and use the same variable, so you don't have to remember a specific constant variable name.
Yes there is. But using SizedBox also wont hurt.
I usually Prefer ListTile for each element in the drawer.
ListTile(
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {},
child: Container(
width: 48,
height: 48,
padding: const EdgeInsets.symmetric(vertical: 4.0),
alignment: Alignment.center,
child: const CircleAvatar(),
),
),
title: const Text('title'),
dense: false,
)

After flutter Upgrade Listtile leading content does not match parent anymore

After doing flutter upgrade yesterday the content of ListTile leading (especially the height) does not match parent anymore.
I have a overflow in height now. Width is working as expected.
As you can see in the picture there is still space below the leading widget.
Does anyone know how to fix this?
Or is there a way to downgrade the flutter version?
Widget _listTile(){
return ListTile(
contentPadding: EdgeInsets.fromLTRB(3.0, 1.0, 0.0, 1.0),
dense: true,
leading: Column(
children: <Widget>[
_leadingButton(),
_leadingProgress
],
),
title: _textSubtitle(subtitle),
subtitle: _textSubtitle(subtitle),
);
}
I believe the change that has caused this is [this one][1]: https://github.com/flutter/flutter/commit/0e2eeb5a485aaccdab7007ad9b90fd8120e77983#diff-53f33273ae4e7462729c5f4b7394428b. If you read the code, it says it was doing an update to adhere to the material spec which means a constraint on the size of the widget, so there's a very good chance it won't change back to how it was. How you were using it was technically against the material spec.
But rather than trying to downgrade flutter, you could simply create your own widget that does the same thing - you don't need to use a ListTile.
Something like this would probably do:
class MyListTile extends StatelessWidget {
final Widget leading;
final Widget title;
final Widget subtitle;
final EdgeInsets padding;
MyListTile({#required this.leading, #required this.title, #required this.subtitle, this.padding});
#override
void build(BuildContext context) {
return Padding(
padding: padding ?? EdgeInsets.zero,
child: Row(
children: [
leading,
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
title,
subtitle,
],
),
),
],
),
);
}
}
Full disclosure though - I haven't run that code or even put it in an IDE so it may have issues, but that should illustrate the basics of what you're trying to do there. You'd probably want to wrap things in padding (most likely at least the leading widget) as needed to get the spacing you currently have.

Flutter, How to make my raw material buttons large and more tightly packed together.

I have some code that creates 7 Raw Material Buttons in the shape of a circle. However I cannot seem to change the size of the circle, or position them closer together.
Page.dart
Row(
children: <Widget>[
new ThemeButton(Colors.red, () => print("red")),
new ThemeButton(Colors.orange, () => print("orange")),
new ThemeButton(Colors.yellow, () => print("yellow")),
new ThemeButton(Colors.green, () => print("green")),
new ThemeButton(Colors.blue, () => print("blue")),
new ThemeButton(Colors.indigo, () => print("pink")),
new ThemeButton(Colors.purple, () => print("purple")),
],
),
ThemeButton.dart
#override
Widget build(BuildContext context) {
return RawMaterialButton (
shape: CircleBorder(),
fillColor: _themeColour,
elevation: 0.0,
highlightElevation: 0.0,
onPressed: () => _onPressed(),
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
);
}
Display:
So the three issue I am facing are all around the size and positioning on the element.
The Circles are too small for my liking.
The space around the circles are too wide.
I can click outside the circle and it will still register the click.
I have looked at the arguments for the Raw Material Button and cannot see what I can change. Adding a padding widget and setting it to 0 doesn't help.
Changing the padding property, unfortunately, did not work for me. However, changing the constraints parameter like in this example turned out to be quite effective:
RawMaterialButton(
constraints: BoxConstraints.tight(Size(36, 36)),
onPressed: null,
child: Icon(Icons.trending_up, size: 18),
shape: new CircleBorder(),
elevation: 0.0,
fillColor: Color.fromARGB(255, 240, 240, 240),
),
Hope it helps!
According to the docs for RawMaterialButton, there should be a padding property that you can set in the constructor, which is typical for this type of component. Try updating the padding value in the constructor of the button instead of adding a new Widget. To be clear, trying setting padding to EdgeInsets.all(0.0).
For further sizing, you can start to look at the Row class properties, specifically MainAxisSize, or wrapping them in variations of the Flexible Widget family.

Position widget in stack with percent

Let's say I want to position a widget inside a Stack but with a percent of the Stack position instead of a fixed size. How to do that in flutter ?
I'd expect that the Positionned.fromRelativeRect constructor would be the thing, using floats between 0 and 1. But seems like no.
Align allows to position the widget in percent. But heightFactor and widthFactor changes the Align size instead of the child size. Which is not what I want.
You can combine a Positioned.fill and LayoutBuilder to achieve such result.
new Stack(
children: <Widget>[
new Positioned.fill(
child: new LayoutBuilder(
builder: (context, constraints) {
return new Padding(
padding: new EdgeInsets.only(top: constraints.biggest.height * .59, bottom: constraints.biggest.height * .31),
child: new Text("toto", textAlign: TextAlign.center,),
);
},
),
)
],
),
one thing that i figured out not long ago is that you can position a widget on the screen using a container its alignment parameter with the help of the Alignment.lerp(x,y,z) function
//the widget will be placed in the center of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 0),
//the widget will be placed in the bottom of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 1),
//the widget will be placed in the bottom quarter of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 0.5),
//the widget will be placed in the top quarter of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, -0.5),
use FractionalOffset & FractionallySizedBox it's very simple in contrast
around no unnecessary code like Positioned.fill
no no extra calculations like Alignment
...
Container(
color: Colors.blue[200],
alignment: FractionalOffset(0.7, 0.6),
child: FractionallySizedBox(
widthFactor: 0.1,
heightFactor: 1/3,
child: Container(color: Colors.red[900])
),
),
...
If you want to use LayoutBuilder then do without Positioned.fill, like this:
you need one LayoutBuilder, no need to turn around every elements and use Transform.translate instead of Padding.
new LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: <Widget>[
Transform.translate(
offset: Offset(
constraints.biggest.width * left,
constraints.biggest.height * top),
child: new Text("toto", textAlign: TextAlign.center,),
),
...
],
);
}
)

Resources