Flutter: DropDownButton items below button - flutter

I want to create a pretty DropDownButton. Unfortunately, this seems to be pretty hard. While the design is fine, whenever I want to select a different item the list drops above the selection in a very ugly way. Below is a picture of the current version.
and after:
Below is my code:
var _repeats = ["Einmalig", "Wiederholen:"];
String _initRepeat = "Einmalig";
FormField<String>(
builder: (FormFieldState<String> state) {
return Container(
decoration: BoxDecoration(
color: Color.fromRGBO(204, 204, 204, 1.0),
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0,),
child: DropdownButton<String>(
dropdownColor:
Color.fromRGBO(204, 204, 204, 1.0),
alignment: AlignmentDirectional.center,
value: _initRepeat,
isDense: true,
onChanged: (String? newValue) {
setState(() {
_initRepeat = newValue!;
state.didChange(newValue);
});
},
items: _repeats.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
color: Colors.black,
fontSize: 12.0,
),
),
);
}).toList(),
),
),
);
},
),
Does anyone have advice on how to improve this? I basically want a selection that is smooth below the currently selected value.
Thank you very much!!

I've created a custom DropdownButton from current version of Flutter's DropdownButton and made it more customizable. It's easy, simple and you can have steady dropdown menu below the button "As long as it's possible" without any issues and many other features described with the package. Also, I've added the same functionality to DropdownButtonFormField2 and added a feature of using the button as a popup menu button and the ability of adding dividers after items. I've tested it very well and it works like a charm!
You can use the package or use the source file on GitHub directly. Also, I've Added custom widget with the package you can customize default DropdownButton2 widget for your entire app and use it with few lines as shown in the examples.
Package: DropdownButton2
Repository (GitHub): DropdownButton2

This package should be your way to go: dropdown_below. It offers exactly what you are looking for.
Or you check out this answer: DropdownButton under Button itself
Basically, it gives you a complete code example of a custom DropdownButton where the Dropdown is always "under" the button itself.

Related

Flutter 2: maxLength inside the textfield without using onChanged

I am trying to find a way to use maxLength and put it inside of the textfield to have a nice rendering. However, I don't want to use the onChanged parameter because I would have to use a setState, but this not the good way to use it.
The parameter maxLength from the flutter's textfield is designed to do not update the state, so that every statefull widgets don't have to rebuild.
So I would like to put the 0/50 inside, at the end of the textfield, on the same line as Title.
From this :
To this :
So somebody have an idea of how to do it without using the parameter onChanged ?
Does flutter has implemented this functionality ?
Try with this, hope you get a solution.
TextField(
maxLength: 250,
buildCounter: (_, {currentLength, maxLength, isFocused}) => Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Container(
alignment: Alignment.centerLeft,
child: Text(currentLength.toString() + "/" + maxLength.toString())),
),
)
Also, Try with this,
TextField(
controller: nameController,
maxLength: 60,
prefixIcon: Icon(
Icons.account_circle,
color: Colors.black45,
),
),

How to Navigate to other Screen using Carousel

I wanted to have a carousel with 5 photos and when I press any of the images I want to navigate to a different screen. I watched many videos but couldn't find any solutions. I used carousel_slider package.
CarouselSlider(
CarouselOptions(height: 400.0),
items: [1,2,3,4,5].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 5.0),
decoration: BoxDecoration(
color: Colors.amber
),
child: Text('text $i', style: TextStyle(fontSize: 16.0),)
);
},
);
}).toList(),
)
Imagine I have 5 different pages and when I press any of the pictures in the carousel then I should be navigated to a particular page. If I use a Gesture detector how can I navigate him using navigator.push?
Try using ManuallyControlledSlider from
https://github.com/serenader2014/flutter_carousel_slider/blob/master/example/lib/main.dart but change onPressed callback inside
Iterable -> RaisedButton to navigate on specific page.

How do I programmatically open and close drawer using keys in Flutter?

I am looking to open and close a drawer programmatically when I press on an icon. I am trying to use keys, but I am not having much luck. I found this article - Flutter: How to open Drawer programmatically ,but I am having issues.
I have a column of icons (see screenshot) that I include in one file and my drawer is in another, both of which are obviously included in a third (which is a fullscreen google map). I am looking at using a global key, but I am unsure where to put everything.
In my map page I have
class _TheMapState extends State<TheMap> with WidgetsBindingObserver {
GlobalKey<ScaffoldState> _drawerKey = GlobalKey();
...
...
When I try to add the key in my drawer page, it doesn't recognise it, so I add the GlobalKey line to my drawer page. I also have to do that to my icon options page too, otherwise the app won't build, due to the errors. There are no errors when I do this, but nothing happens when I click on the button.
Here is where I added the key to the drawer.
Widget mapDrawer() {
return Drawer(
key: _drawerKey,
...
...
Here is my icon code, which is in a separate file.
Widget _buildShowHideDrawerIcon() {
return Container(
child: Opacity(
opacity: 0.60,
child: Container(
padding: EdgeInsets.all(7.0),
decoration: BoxDecoration(
color: Colors.white,
//border: Border.all(color: Colors.black12, width: 1.0),
boxShadow: [
BoxShadow(
color: Colors.black,
offset: Offset(0.0, 0.0),
blurRadius: 3.0,
),
],
),
child: InkWell(
onTap: () {
print("SHOW/HIDE DRAWER ICON PRESSED");
setState(() {
_drawerKey.currentState.openDrawer();
});
},
child: Icon(
Icons.swap_horiz,
color: Colors.black87,
size: 24.0,
semanticLabel: 'Show ,or hide sliding drawer',
),
),
),
),
);
}
I would appreciate any help on this.
Assign the key to Scaffold, not Drawer
Scaffold(
key: _drawerKey,
drawer: Drawer(),
You should assign the key to Scaffold, not Drawer
Scaffold(
key: _drawerKey,
And replace the line:
_drawerKey.currentState.openDrawer();
With
_drawerKey.currentState?.openDrawer();

Flutter extract widget requires a return widget

I am getting a lot of widgets groups in the tree and wanted to start extracting them to their own widget.
I tried with Ctrl-Alt-W, this command does nothing and shows no error when there is a problem.
If you go to the top menu and click on refactor, extract, extract to widget the following error occurs:
Can only extract a widget expression or a method returning widget.
This is an example of the code I am trying to extract:
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
right: 8.0,
top: 8.0,
bottom: 4.0),
child: new TextField(
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
filled: true,
hintStyle: new TextStyle(
color: Colors.grey[800]),
hintText: "Supervisor",
fillColor: Colors.white70),
),
),
),
),
How would I modify this code in the widget tree to have a return so that it can be easily extracted.
Thanks
I don't think this is a bug. Everyone says that you have to highlight all of the code to extract it, and it worked with them, but for me, I've made it like this.
First I put the cursor at the specific widget.
Ensure Two Steps:
Your Code is 'Dart Formatted' (if using AndroidStudio, else regular formatted with appropriate commas if using VSCode), and
Your Cursor is at the beginning of the widget code block that you want to extract as a widget. Cursor position is the only problem here. Even after selecting the entire code block, if we right click while cursor is somewhere in the middle of the code block, refactoring and widget extraction wont work.
Dont select the block, only ensure that cursor is at the first letter of the block. Then -> right click -> refactor -> extract widget and voila! It will work. Thanks. Happy Coding!

How to fix "BoxConstraints forces an infinite width" and how to delete an item from a list?

I am a beginner in Flutter and I can not get the result I want because I have a "layout problem". I tried several things for hours...
Technical problem:
When I click on "add player button" to add a new TextField to set more play name, I have an error: "BoxConstraints forces an infinite width". So, the TextField isn't displayed.
Other features that I do not know how to realize:
- I have a "dropdownmenu' to choose difficulty game. Why the default text isn't displayed (Difficulty: normal)?
- How to do this: When the user clicks on the icon "remove" (see suffixIcon on TextField), then the corresponding fieldText is deleted?
- How to do this: When user click to add player the default HintText is incremented (Player 2, Player 3, Player 4, ....)?
- Why my background gradient doesn't fix when user scroll page? (like in HTML/CSS 'background-attachment: fixed') ?
import 'package:flutter/material.dart';
/// Styles
///
class Homepage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<Homepage> {
String dropdownValue = 'Normal';
List<TextField> _playerList = new List(); //_playerList is my List name
int playerListIndex = 0; //Count the number of times "Add" button clicked
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: <Widget>[
// To have a gradient background
new Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.1, 1],
colors: [
Colors.redAccent[200],
Colors.red[300],
],
tileMode:
TileMode.repeated,
),
),
//I need SingleChildScrollView to haven't "Overflow yellow bar on bottom screen"
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// Box to add big image
SizedBox(
height: 450,
),
// DROP DOWN MENU to choose difficulty modes
InputDecorator(
decoration: InputDecoration(
prefixIcon: const Icon(Icons.games),
filled: true,
),
child: DropdownButtonHideUnderline(
child: new DropdownButton<String>(
value: dropdownValue,
hint: Text('Difficulty normal'),
isDense: true,
items: <String>['Normal', 'Easy', 'Hard']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
),
),
),
//Field player name 1. At least I need 1 player
new TextField(
maxLength: 20,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
filled: true,
hintText:"Player 1",
counterText: "",
),
),
//Field for other player name 2, 3, 4, ...
new ListView.builder(
padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _playerList.length,
itemBuilder: (context, index) {
TextField widget = _playerList.elementAt(index);
return widget;
}),
//Button to add other field text to set player name. The user can delete a player on click on "clear icon button"
new Align(
alignment: Alignment.centerRight,
child: FlatButton.icon(
color: Colors.black,
icon: Icon(
Icons.add,
color: Colors.white,
size: 18,
),
label: Text('Add player',
style:
TextStyle(fontSize: 12, color: Colors.white)),
onPressed: () {
setState(() {
playerListIndex++;
_playerList.add(
new TextField(
maxLength: 20,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
suffixIcon: new IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_playerList.removeAt(playerListIndex); //Doesn't work
}),
filled: true,
hintText: "Player $playerListIndex", //Doesn't work i would like Player 2, Player 3, ...
counterText: "",
),
),
);
print(playerListIndex);
print(_playerList);
print(_playerList[0]);
});
}),
),
new ButtonTheme(
minWidth: 350,
height: 45,
child: FlatButton(
color: Colors.black,
child: Text('Play',
style:
TextStyle(fontSize: 18, color: Colors.white)),
onPressed: () {
// Perform some action
},
),
),
],
),
),
),
),
),
]),
);
}
}
I think that I understood what's going on. Let's begin:
First. Your TextField is not showing up and you're getting an error because you didn't set a width and a height for it, so, it's trying to get the maximum size as it can get. Solution for this? Wrap it in a Container widget (or SizedBox) and give it a width and a height. That should fix the first problem.
Second. Your DropDownMenu's hint text (Difficulty Normal) is not showing because you're not passing the parameter "hint" in the DropDown widget, See the hint propierty below.
Third. You want to clear the TextField string when click on a suffix, right? To achieve this, pass a GestureDetector or an IconButton as your suffix component, create a TextEditingController and pass it in the TextField, in the controller section, then, on the onPressed/onTap method (depending on which Widget you'll set) set this: _textController.text.clear() method, inside the setState function. This should fix the problem.
Forth. You want the list of player's string to increase as you add more players right? You have many ways to achieve this, one of them is creating an empty list, you can mock to test it, and then, everytime you click the "Add player" button, you'll add a String corresponding to the value inside a for loop passing the for's index as int value in the "Player $index". Again, the for method will receive a setState method, and inside the setState, your logic to add one more Player.
And finally, your gradient does not keep fixed? Hmm, I think you can pass a proprierty in the Scaffold widget called resizeToAvoidBottomInset and set it to false.
If something did not work as you expected, reply it and I'll help you out.