Flutter DropdownMenuItem in separate widget file - flutter

I have an issue with refactoring my code of DropdownButton widget in Flutter. I have simple DropdownButton.
DropdownButton(
items: [
DropdownMenuItem(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Ascending'),
if (widget.currentDateSortOrder == SortOrderType.Ascending)
Icon(Icons.check)
],
),
),
value: 'Asc',
),
DropdownMenuItem(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Descending'),
if (widget.currentDateSortOrder == SortOrderType.Descending)
Icon(Icons.check)
],
),
),
value: 'Desc',
)
],
onChanged: (itemIdentifier) {
...
},
)
I want to move DropdownMenuItem to separate widget to make my widget tree leaner. So I then moved it.
import 'package:flutter/material.dart';
class FileListDropdownMenuItem extends StatelessWidget {
final String labelText;
final bool showSelectedMark;
final String itemValue;
FileListDropdownMenuItem(this.labelText, this.showSelectedMark, this.itemValue);
#override
Widget build(BuildContext context) {
return DropdownMenuItem(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(labelText),
if (showSelectedMark)
Icon(Icons.check)
],
),
),
value: itemValue,
);
}
}
And when I'm trying to use it in DropdownButton like this:
...
items: [
FileListDropdownMenuItem(
'Ascending',
widget.currentDateSortOrder == SortOrderType.Ascending,
'Asc')
],
...
I get this error:
The argument type 'List<FileListDropdownMenuItem>' can't be assigned to the parameter type 'List<DropdownMenuItem<dynamic>>'.
Is there a way to make such approach work? I know that I can leave DropdownMenuItem in DropdownButton and move only 'child' property of it to the separate widget. However then I would have to manage 'value' and other properties of DropdownMenuItem in the main file.

DropdownButton requires its items to be List<DropdownMenuItem>. But your class, FileListDropdownMenuItem, extends just StatelessWidget. If you want to use it as replacement for DropdownMenuItem, you should extend it:
class FileListDropdownMenuItem extends DropdownMenuItem {
FileListDropdownMenuItem(
String labelText,
bool showSelectedMark,
String itemValue,
) : super(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(labelText),
if (showSelectedMark)
Icon(Icons.check)
],
),
),
value: itemValue,
);
}

Related

How to wrap a list of widgets with Flexible

I have a StatelessWidget defined as:
class FlexElement extends StatelessWidget {
final List<Widget> widgets;
final bool isVisible;
const FlexElement({
Key? key,
required this.widgets,
required this.isVisible,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Visibility(
visible: isVisible,
child: Container(
child: Row(
children: <Widget>[...widgets],
),
),
);
}
}
I would like to wrap each widget with a Flexible, so it doesn't overflow. (Row(children: Flexible([...widgets])). But since it is a list of widgets, I can only assign to multiple children and not to a single child. How would I solve this?
This is the result i would like:
Row(
children: [
Flexible(
child: Widget1(), // <-- Wrapped in Flexible.
),
Flexible(
child: Widget2(), // <-- Wrapped in Flexible.
),
...
],
)
You can do,
children: <Widget>[...widgets.map((w)=>Flexible(child:w))],
Or direct
children:widgets.map((w)=>Flexible(child:w)).toList(),
This is what I always do:
Row(
children: [
Flexible(
flex: 20,
fit: FlexFit.tight,
child: Widget1(), // <-- Wrapped in Flexible.
),
Flexible(
flex: 30,
fit: FlexFit.tight,
child: Widget2(), // <-- Wrapped in Flexible.
),
...
],
)
I put the flex values to total 100 so I think of each as a percentage of the width of the Row.

Add scrollable and refreshable data table between fixedWidth widgets

Totally I need a scrollable DataTable with refresh possibility on pull down between header and footer fixed width widgets.
I have a widget, using on every page in my app. THis widget return a Scaffold with appbar and body like column (). This widget used like CommonPage(title, widgetsArrayForBodyColumn) from other widgets.
For now I need to use a follow widgets in that body:
Lookup (for filtering or for example, any fixed height widget)
DataTable (for show data)
Card (to show another data)
DataTable should be scrollable and refreshable on pull down.
I tried many different ways, but still has no success. My last solution is using stack, but list hidden beside the card.
I can't understand how to achieve my goal: have refreshing datatable between non scrollable widgets in a column.
class CommonPage extends StatefulWidget {
final String title;
final List<Widget> children;
const CommonPage({
Key? key,
required this.title,
required this.children,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: Column(children: [
FixedHeightWidget(),
PageScreen(),
]);
}
}
class PageScreen {
#override
Widget build(BuildContext context) {
return CommonPage(
title: Title,
children: [
FixedHeightWidget(),
PageList(),
],
);
}
}
class PageList {
Widget build(BuildContext context) {
//TODO Return fixedWidthWidget at top, expandedRefreshableScrollableDataTable, fixedWidthWidget at bottom
}
}
My current solution: (almost achieves my goal, but as mentioned card hides part of list and no ways to scroll and check bottom of the list):
return Expanded(
child: Stack(
children: [
RefreshIndicator(
onRefresh: onRefresh,
child: ListView(
children: [
listItems.isNotEmpty
? DataTable(...)
: Container(),
],
),
),
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
alignment: Alignment.bottomCenter,
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Card(...),
),
),
),
],
),
],
),
);
I found the solution:
return Expanded(
child: Column(
children: [
Expanded(
child: RefreshIndicator(
onRefresh: onRefresh,
child: ListView(
children: [listItems.isNotEmpty
? DataTable(...)
: Container(),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Card(...),
),
],
),
);

Can I random to show SizeBox widget?

I want to show SizeBox widget by random. when I open this class it should be random to show Size Box 1 first or Size Box 2 first.
class Page extends StatelessWidget {
var random = new Random();
int randomNumber;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
randomNumber = random.nextInt(1);
if(randomNumber == 1){
/////////////////////////////////////// Size box 1 ////////////////////////////////////
SizedBox(
child: TextButton(
onPressed: () {
AlertDialog(
title: Text('a'),
content: Text('a'));
},
child: Image.asset('images/test.jpg'),
)),
}else{
/////////////////////////////////////// Size box 2 ////////////////////////////////////
SizedBox(
child: TextButton(
onPressed: () {
AlertDialog(
title: Text('b'),
content: Text('b'));
},
child: Image.asset('images/test2.jpg'),
)),
}
I try to use randomNumber = random.nextInt(1) in children: <Widget>[] but it show
The element type 'int' can't be assigned to the list type 'Widget'.dart(list_element_type_not_assignable)
It seems I cannot write dart code in widget. Can I random to show SizeBox widget ?
The error occurs because you called randomNumber = random.nextInt(1); inside the Row children. Since randomNumber is of type int and not of type Widget you can't do this. One way to fix this is to create a StatefulWidget and set the randomNumber in initState method.
By the way using if/else inside children of Row/Column you don't put the curly brackets {}.
import 'package:flutter/material.dart';
import 'dart:math';
class Page extends StatefulWidget {
#override
_Page createState() => _Page();
}
class _Page extends State<Page> {
var random = new Random();
int randomNumber;
#override
void initState(){
randomNumber = random.nextInt(1);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
if (randomNumber == 1)
/////////////////////////////////////// Size box 1 ////////////////////////////////////
SizedBox(
child: TextButton(
onPressed: () {
AlertDialog(title: Text('a'), content: Text('a'));
},
child: Image.asset('images/test.jpg'),
),
)
else
/////////////////////////////////////// Size box 2 ////////////////////////////////////
SizedBox(
child: TextButton(
onPressed: () {
AlertDialog(title: Text('b'), content: Text('b'));
},
child: Image.asset('images/test2.jpg'),
),
),
],
),
),
],
),
),
);
}
}

Flutter: Row is stepping a line inside a Wrap

suppose we have:
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Wrap(
children: [
Row(
children: [
Text('a'),
],
),
Row(
children: [
Text('b'),
],
),
],
);
}
}
the output of this would be:
notice how each of the Row's is taking a whole line
how can I fix this and make them side by side?
you should wrap your Row's with FittedBox, which will make them only consume the space they need, so your code should be like this:
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Wrap(
children: [
FittedBox(
child: Row(
children: [
Text('a'),
],
),
),
FittedBox(
child: Row(
children: [
Text('b'),
],
),
),
],
);
}
}
and your output will be like this:

"How to create two text widgets in a row widget"

"I'm a beginner in flutter, I want to display two text in row .where "hello" is the first text widget and "world" is another text widget
I tried doing this with my basic knowledge but I came across these errors
Horizontal RenderFlex with multiple children has a null textDirection, so the layout order is undefined.
'package:flutter/src/rendering/flex.dart':
Failed assertion: line 439 pos 18: 'textDirection != null'
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
Text("hello",textDirection: TextDirection.rtl,),
Text("world",textDirection: TextDirection.rtl,)
],
);
}
}
Maybe you want to use RichText instead?
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(text: 'hello', style: TextStyle(color: Colors.red)),
TextSpan(text: ' world', style: TextStyle(color: Colors.blue)),
],
),
)
Please read this link about Row in Flutter:
https://medium.com/jlouage/flutter-row-column-cheat-sheet-78c38d242041
In a nutshell, you Declare a Row and the items that will be inside that row are the children (Which must be widgets).
I picked an example from here
https://flutter.dev/docs/development/ui/layout
something like this
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);
In Your case, you just have to erase the image.asset and change it for texts...
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Hello'),
Text('World'),
],
);
or whatever you want.
Please try below code:-
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Hello",style: TextStyle(color: Colors.blue)),
Text("World",style: TextStyle(color: Colors.blue))
],
);
}
**Try This Simplest Way**
import 'package:flutter/material.dart';
class Example extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Row(
children: <Widget>[
Text("Hello"),
Text("World"),
],
),
),
);
}
}