I have a screen design, below, that the team built for an app that stores a list of names vertically down on one side, and then continues on the other.
The solution we currently have in place was to create a generated List of a fixed size, and store a counter starting at one, incrementing x-number of values on the left (10 in our example) and then continuing the count for the list generation on the right; code sample to follow.
The solution works for our very basic scenario, however, we now have a need to wrap the rendering of the widget with a StreamBuilder as we'll be dynamically collecting information from a stream on interval, to populate the table with values, which is causing us severe headaches.
We looked into a GridView, however, that only allows us vertical placement of elements (left -> right, left -> right) instead of down (left) down (right).
An example of our list generation:
List<Widget> _tableRow() {
if (_counter >= 20) {
_counter = 1;
}
List<Widget> widgets = List.generate(10, (index) {
var w = Expanded(
child: Row(
children: [
Expanded(
flex: 0,
child: Container(
alignment: Alignment.center,
width: 50,
decoration: BoxDecoration(
color: kineticOffBlack,
border: Border(
right: const BorderSide(
color: Colors.black,
width: 2,
),
bottom: (index < 9)
? const BorderSide(
color: Colors.black,
width: 2,
)
: BorderSide.none,
),
),
child: Label.headingFour('$_counter'),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: kineticOffBlack,
border: Border(
right: const BorderSide(
color: Colors.black,
width: 2,
),
bottom: (index < 9)
? const BorderSide(
color: Colors.black,
width: 2,
)
: BorderSide.none,
),
),
child: Label.headingFour(tableNames[_counter - 1]),
),
),
Expanded(
flex: 0,
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: kineticOffBlack,
border: Border(
bottom: (index < 9)
? const BorderSide(
color: Colors.black,
width: 2,
)
: BorderSide.none,
right: const BorderSide(
color: Colors.black,
width: 2,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Label.headingFour(
scoreByMetric(
tableNames[_counter - 1], MetricType.intensity),
color: kineticGold,
textAlign: TextAlign.center,
),
const SizedBox(width: 5),
const Icon(Icons.arrow_upward, color: kineticOffBlack),
],
),
),
),
],
),
);
_counter++;
return w;
});
return widgets;
}
This block of code creates the basic layout (table + 10 rows), and is then reused again to create the right side:
Widget _buildTable() {
return Row(
children: [
Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(
color: kineticOffBlack,
border: Border(
left: BorderSide(
color: Colors.black,
width: 2,
),
right: BorderSide(
color: Colors.black,
width: 1,
),
bottom: BorderSide(
color: Colors.black,
width: 2,
),
top: BorderSide(
color: Colors.black,
width: 1,
),
),
),
child: Flex(
direction: Axis.vertical,
children: _tableRow(),
),
),
),
Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(
color: kineticOffBlack,
border: Border(
left: BorderSide(
color: Colors.black,
width: 1,
),
right: BorderSide(
color: Colors.black,
width: 2,
),
bottom: BorderSide(
color: Colors.black,
width: 2,
),
top: BorderSide(
color: Colors.black,
width: 1,
),
),
),
child: Column(
children: _tableRow(),
),
),
)
],
);
}
This is then use to build the table, a Row with 2x Expanded widgets, this was just what we came up with.
This works, like I said, however, we have a need to wrap the return in _buildTable() with a StreamBuilder so we could do additional manipulation on the widget as data is streamed to the application from a queuing service provided by a 3rd party vendor.
Our challenge is that we have 2x _tableRow() methods, which would need to be managed separately, when the ideal solution would be to either have a single _tableRow method that creates the desired view, or using Stream.listen() in our initState() which could, in itself be another stumbling block.
The ask here is, if any Flutter-guru out there might have a simpler solution to building the desired table, without the sorcery we had to put in place in _buildTable()?
The table design:
Related
I'm new in Flutter,
I have a Container which child is Stack that using Positioned inside.
My problem is Container just taking Non-Positioned widget size.
Here is my problem:
I want something like this:
Here is my Widget code:
class CheckUpItem extends StatelessWidget {
const CheckUpItem({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
width: 100,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
color: Colors.white,
border: Border.fromBorderSide(
BorderSide(
width: 1,
color: Color(0xFFBAD9F8),
),
),
boxShadow: [
BoxShadow(
color: Color(0x00000029),
offset: Offset(2, 3),
blurRadius: 2,
),
],
),
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(4),
topRight: Radius.circular(4),
),
child: Container(
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
border: Border(
bottom: BorderSide(
width: 1,
color: Color(0xFFBAD9F8),
),
),
),
child: Consumer(
builder: (context, ref, child) {
return Center(
child: Text(
'BMI',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
height: 1.43,
color: Color(0xFF6B6B6B),
),
),
);
},
),
),
),
Container(
padding: const EdgeInsets.only(top: 5.5, bottom: 4),
child: Column(
children: [
Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 36,
height: 36,
decoration: const BoxDecoration(
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
width: 2,
color: Color(0xFFBF3D3D),
),
),
color: Color(0xFFF86060),
),
child: const Center(
child: Text(
'B',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
height: 1,
fontSize: 20,
),
),
),
),
Positioned(
bottom: -6,
left: 26,
child: Container(
width: 22,
height: 22,
padding: const EdgeInsets.fromLTRB(1, 0, 3, 2),
decoration: const BoxDecoration(
border: Border.fromBorderSide(
BorderSide(
width: 1,
color: Color(0xFF6A8FD4),
),
),
color: Color(0xFFE0EEFC),
shape: BoxShape.circle,
),
child: SvgPicture.asset(ImageAssets.speaker),
),
),
],
),
const SizedBox(height: 6),
],
),
),
],
),
),
);
}
}
I'm stucking all day with this, I'm tempary using Column and const SizedBox(height: 6) for fake height.
How to make it right way?
P/s: Sorry, my English is not good!
Because every thing is static hear and you know how much the svg container going down, you can calculate it and add it to padding bottom, like this:
Container(
padding: const EdgeInsets.only(top: 5.5, bottom: 10), // 4 + 6(Positioned bottom)
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 36,
height: 36,
decoration: const BoxDecoration(
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
width: 2,
color: Color(0xFFBF3D3D),
),
),
color: Color(0xFFF86060),
),
child: const Center(
child: Text(
'B',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
height: 1,
fontSize: 20,
),
),
),
),
Positioned(
bottom: -6,
left: 26,
child: Container(
height: 22,
width: 22,
decoration: const BoxDecoration(
border: Border.fromBorderSide(
BorderSide(
width: 1,
color: Color(0xFF6A8FD4),
),
),
color: Color(0xFFE0EEFC),
shape: BoxShape.circle,
),
child: SvgPicture.asset(
'assets/images/tes.svg',
),
),
),
],
),
),
Trying to do a windows themed app and i've been trying to allign the dropdown menu items to behave like like how the windows ones do
Any ideas ?
This is what i want it to look like
Code:
///Baudrate
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text("BaudRate"),
Align(
alignment: Alignment.centerLeft,
child: Container(
width: 150,
height: 30,
decoration: BoxDecoration(
color: Colors.grey.shade400,
border: Border(
top: BorderSide(color: Flutter95.grays[0], width: 1),
left: BorderSide(color: Flutter95.grays[0], width: 1),
bottom: BorderSide(color: Flutter95.grays[2], width: 3),
right: BorderSide(color: Flutter95.grays[2], width: 3),
),
),
child:Center(
child: DropdownButton(
style: TextStyle(fontSize: 16,color: Colors.black),itemHeight: 50,
value: _selectedBaudrate,
onChanged: (newValue) {setState(() {_selectedBaudrate = newValue.toString();});},
items: _comportBaudrate.map((String item) => DropdownMenuItem<String>(
value: item,
child: Text("$item Baud"),
),).toList(),),
),
),
),
I want to make outline border and label to container like this
You can do this for simulate this design. It looks little bit of complicate but I can't find other way to do this. Hope that it will work for you.
Just make sure the bottom and the top container colors are same as the code.
Just call this MyMenuContent class to see this output.
class MyMenuContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: MediaQuery.of(context).size.height / 2,
padding: EdgeInsets.all(10),
child: Stack(
children: [
Positioned(
top: 50,
child: Container(
height: MediaQuery.of(context).size.height / 12,
width: MediaQuery.of(context).size.width / 2,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(
color: Colors.black,
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.calendar_today),
SizedBox(width: 10),
//Text
Text(
'16-12-2020',
style: TextStyle(
fontSize: 17, fontWeight: FontWeight.w500),
)
],
),
),
),
Positioned(
// top: 40,left: 80,
top: MediaQuery.of(context).size.width / 9.5,
left: MediaQuery.of(context).size.height / 7,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
"outline",
style: TextStyle(fontSize: 15),
)),
),
)
],
),
),
),
);
}
}
I am trying to apply the same concept of instagram to go from one feed to another.
Here i have like quiz1 part and i want to go to the another quiz2 section on tapping to to the right side and after pressing left side i want to go for quiz1 section.
Widget mainQuiz() {
return Column(
children: [
Spacer(
flex: 1,
),
Container(
padding: EdgeInsets.all(30),
child: Text(
"When was the first spaceship NOT launched?",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 24,
),
),
),
/* ---------------------OPTION 1----------------------*/
Container(
margin: EdgeInsets.only(
left: 65,
right: 65,
bottom: 15,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(100)),
child: Row(
children: [
Container(
margin: EdgeInsets.all(10),
child: Row(
children: [
Stack(
children: [
Container(
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white),
shape: BoxShape.circle,
),
child: Icon(
Icons.check,
size: 15,
color: Color(0xffFFC700),
),
)
],
),
Text(
"Last Tuesday",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
],
),
),
Spacer(),
Container(
constraints: BoxConstraints.expand(
height: 40,
width: 50,
),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topRight: Radius.circular(100),
bottomRight: Radius.circular(100),
),
),
child: Center(
child: Text(
"55%",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
),
],
),
),
/* ---------------------OPTION 2----------------------*/
Container(
margin: EdgeInsets.only(
left: 65,
right: 65,
bottom: 15,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(100)),
child: Row(
children: [
Container(
margin: EdgeInsets.all(10),
child: Row(
children: [
Stack(
children: [
Container(
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white),
shape: BoxShape.circle,
),
child: Icon(
Icons.check,
size: 15,
color: Color(0xffFFC700),
),
)
],
),
Text(
"1969",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
],
),
),
Spacer(),
Container(
constraints: BoxConstraints.expand(
height: 40,
width: 50,
),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topRight: Radius.circular(100),
bottomRight: Radius.circular(100),
),
),
child: Center(
child: Text(
"45%",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
),
],
),
),
/* ---------------------OPTION 3----------------------*/
Container(
margin: EdgeInsets.only(
left: 65,
right: 65,
bottom: 15,
),
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(100)),
child: Row(
children: [
Stack(
children: [
Container(
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
shape: BoxShape.circle,
),
child: SizedBox(
height: 15,
width: 15,
),
)
],
),
Text(
"1957",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
],
),
),
Spacer(
flex: 2,
),
],
);
}
like this is quiz one section i want to go to the second quiz section which i can take as same ui
I would wrap your "mainQuiz" inside a Stack widget.
In that way you could add other 2 Containers inside the Stack's children.
Those containers could be positioned using "Positioned" widget.
One on the left of the screen and one on the right.
You should give then the size too.
I would let them transparent and I would give them a "GestureDetector" or something to detect the tap and call the Navigator to change screen.
As per your requirement, I think you should use
PageView
By default it has the swipe feature to go to the next and previous screen.
EDIT:-
As per you requirements, you know that you have a screen which already have interaction in form of giving answers, which has to be done using clicks. Though you can place empty containers in stack to implement clicks for left right, but as container need to have some width, thus they will overlap with you answers because basic padding is not more than 20. Thus I think you should go for swipe or specified buttons to go the left right i.e next and previous question.
Container(
// decoration: BoxDecoration(
// border: Border.all(color: Colors.black45),
// borderRadius: BorderRadius.circular(8.0),
// ),
child: Row(
children: <Widget>[
Container(
child: Text("hi"),
margin : EdgeInsets.fromLTRB(20, 8, 8, 16),
width: MediaQuery.of(context).size.width *0.42,
height: 90,
color: Colors.black12,
),
Container(
child: Text("Hi"),
margin: EdgeInsets.fromLTRB(16, 8, 8, 16),
width: MediaQuery.of(context).size.width * 0.42 ,
height: 90,
color: Colors.black12,
)
],
),
),
I can add border using Box decoration on the outer container, but it's throwing me an error when I am trying to do the same on the inner containers. What is the problem and how to solve it?
In order to add border on container inside row widget , we have to use decoration for the inner containers.
Once you will post the error, we can answer you better but i think the below code will be helpful for you.
If you are using decoration then you must not add colour attribute in container directly, it should be in decoration only.
Container(
child: Row(
children: <Widget>[
Container(
child: Text("hi"),
margin: EdgeInsets.fromLTRB(20, 8, 8, 16),
width: MediaQuery.of(context).size.width * 0.42,
height: 90,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.blue,
width: 4,
)),
),
Container(
child: Text("Hi"),
margin: EdgeInsets.fromLTRB(16, 8, 8, 16),
width: MediaQuery.of(context).size.width * 0.42,
height: 90,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.blue,
width: 4,
)),
)
],
),
),
In container widgets you cannot use the color and decoration at the same time. Remove the color property from the Container and move it into the BoxDecoration widget
This should work:
Container(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black45),
borderRadius: BorderRadius.circular(8.0),
color: Colors.black12, //add it here
),
child: Text("hi"),
margin : EdgeInsets.fromLTRB(20, 8, 8, 16),
width: MediaQuery.of(context).size.width *0.42,
height: 90,
//color: Colors.black12, //must be removed
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black45),
borderRadius: BorderRadius.circular(8.0),
color: Colors.black12, //add it here
),
child: Text("Hi"),
margin: EdgeInsets.fromLTRB(16, 8, 8, 16),
width: MediaQuery.of(context).size.width * 0.42 ,
height: 90,
//color: Colors.black12, // must be removed
)
],
),
),
Consider the humble Divider widget to keep things simple. If you add it to the bottom of a Column in your row it will add a line that will act as a border.
const Divider(height: 1.0,),