I want to make my custom-widget respond to touch, hence I used GestureDetector's onTap method. It's working but it is also changing the dimensions of my custom widget, which I don't want. I am posting some code and screen-shots to make the situation more clear.
What I want is this, just a MALE card (my custom-widget called ReusableCard).
After wrapping my ReusableCard widget in a GestureDetector, what I get is this.
I am sharing my code here. In this code I haven't used GestureDetector yet.
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(
child: Text('BMI Calculator')
),
),
body: Column(
children: [
Expanded(
child: Row(
children: [
ReusableCard(activeCardColor,
IconContents(FontAwesomeIcons.mars, 'MALE')),
ReusableCard(activeCardColor,
IconContents(FontAwesomeIcons.venus, 'FEMALE')),
],
),
),
Expanded(
child: Row(
children: [
ReusableCard(activeCardColor),
],
),
),
Expanded(
child: Row(
children: [
ReusableCard(activeCardColor),
ReusableCard(activeCardColor),
],
),
),
Container(
color: bottomContainerColor,
width: double.infinity,
margin: EdgeInsets.only(top: 10),
height: bottomContainerHeight,
),
],
),
);
}
}
The real meaty part of the code is in Blockquotes. Meaty Part after adding GestureDetector to MALE ReusableCard.
GestureDetector(
child: ReusableCard(activeCardColor,
IconContents(FontAwesomeIcons.mars, 'MALE')),
),
ReusableCard(activeCardColor,
IconContents(FontAwesomeIcons.venus, 'FEMALE')),
I don't think if it's helpful but I am also sharing my ReusableCard code with you guys.
class ReusableCard extends StatelessWidget {
ReusableCard(this.colour, [this.cardChild]);
final Color colour;
final Widget cardChild;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: cardChild,
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(10),
),
),
);
}
}
I have checked the documentation and I seem to be doing everything correctly. What I can infer from this is that I am messing up somewhere with the Expanded widget, but cannot correct it. Any help appreciated folks.
I would suggest adding the GestureDetector inside your ReusableCard, as a child of your Expanded.
class ReusableCard extends StatelessWidget {
ReusableCard(
this.colour, {
required this.cardChild,
required this.onTap,
});
final Color colour;
final Widget cardChild;
final VoidCallback onTap;
#override
Widget build(BuildContext context) {
return Expanded(
child: GestureDetector(
onTap: onTap,
child: Container(
child: cardChild,
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(10),
),
)),
);
}
}
This way you won't need to wrap your ReusableCard with a GestureDetector breaking the Expanded widget.
Related
Please look at this image Home Page
Now when I scroll the ListView it becomes like this -
Home Page
Now I know the reason why this is happening, it is because I used ListView as a parent to this entire view and added ListView.builder() and other widgets as its child.
What I want is to scroll the ListView.builder() without scrolling the entire page.
For this I first tried to use Column as parent but that ended up giving the overflow pixels error.
And then I set the physics: const NeverScrollableScrollPhysics() inside the parent ListView but after that it made my ListView.builder() to show all of its list items.
Here is my Code for Home Screen
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 45, bottom: 24),
children: [
header(),
const SizedBox(height: 36),
const BalanceCard(),
const SizedBox(height: 36),
Recent()
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: bottomNavigationBar(),
),
],
),
); }
Recent List Code
class RecentItems extends StatefulWidget {
final List<Transaction> transactions;
RecentItems({required this.transactions});
#override
State<RecentItems> createState() => _RecentItemsState();
}
class _RecentItemsState extends State<RecentItems> {
#override
Widget build(BuildContext context) {
return SizedBox(
height: 450,
child: Expanded(child: ListView.builder(
itemBuilder: (context, index) {
final item = widget.transactions[index].toString();
return Dismissible(
direction: DismissDirection.endToStart,
key: UniqueKey(),
onDismissed: (direction) {
setState(() {
widget.transactions.removeAt(index);
});
// Then show a snackbar.
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Transaction Deleted')));
},
background: Container(
color: Colors.red,
alignment: AlignmentDirectional.centerEnd,
child: const Padding(
padding: EdgeInsets.fromLTRB(0.0, 0.0, 15.0, 0.0),
child: Icon(
EvaIcons.trash2,
color: Colors.white,
),
),
),
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: ListTile(
leading: CircleAvatar(
radius: 30,
foregroundImage: widget.transactions[index].Image,
backgroundColor: primaryColor,
),
title: Text(
widget.transactions[index].title,
style: const TextStyle(color: secondaryColor),
),
subtitle: Text(
DateFormat.yMMMd().format(widget.transactions[index].date),
),
trailing: Text(
'\$${widget.transactions[index].amount}',
style: const TextStyle(color: secondaryColor),
),
),
),
);
},
itemCount: widget.transactions.length,
),)
);
}
}
Recent Widget -
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Recent Transactions',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: secondaryColor),
),
const SizedBox(height: 5),
RecentItems(transactions: _userTransactions),
],
),
)
The entire screen scrolls because Recent() is included in the same ListView as header() and balanceCard().
Try something like this:
Scaffold(
body: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 45, bottom: 24),
children: [
header(),
const SizedBox(height: 36),
const BalanceCard(),
const SizedBox(height: 36),
],
),
),
// Recent items removed from ListView
Recent(),
Align(
alignment: Alignment.bottomCenter,
child: bottomNavigationBar(),
),
],
),
)
I hope this helps.
By simplifying your code, this is an example of a layout where you have a single Column in the Scaffold. The Column contains some sized, unsized and aligned children.
One child, Recent is a ListView, without explicit height, but wrapped into an Expanded widget. This way it will occupy all the remaining area left by the other children, and it will be scrollable.
(You will run into trouble with this if the children without Recent occupy all the available area.)
Please have a look at this code, you can copy-paste it into a DartPad fiddle:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => Scaffold(
body: Column(
children: [
const Text('header()'),
const SizedBox(height: 36),
const Text('BalanceCard()'),
const SizedBox(height: 36),
Expanded(child: Recent()),
const Align(
alignment: Alignment.bottomCenter,
child: Text('bottomNavigationBar()'),
),
],
),
);
}
class Recent extends StatelessWidget {
#override
Widget build(BuildContext context) => ListView.builder(
itemCount: 100, itemBuilder: (context, index) => Text('Item $index'));
}
I have an application like this:
My aim is that when I press the eye icon next to the text "Hello", I want a box to open just below the text and write the German version of "Hello". So it will say "Hallo".
My purpose is to show the meaning of the word.
When I press the eye, I want to show the German of the word. How can I make a white box under the word Hello, that is, the box in which the German language will be written?
Codes:
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
class selamlasmaLearn extends StatelessWidget {
List <wordAndMeaning> wordsList = [wordAndMeaning("Hello", "Hallo"), wordAndMeaning("Go", "Gehen")];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) {
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
word.word,
style: TextStyle(fontSize: 45, color: Colors.white),
),
SizedBox(width: 10,),
Icon(Icons.remove_red_eye_sharp, color: Colors.white, size: 25,), // <<<<<<<<<
],
),
),
);
},
);
}).toList(),
);
}
),
);
}
}
class wordAndMeaning {
String word;
String meaning;
wordAndMeaning(this.word, this.meaning);
}
I keep the word and its German in a list called wordsList.
Thanks for the help in advance.
You can convert the widget to StatefulWidget or use a ValueNotifier to control the preserve/notify the state visibility.
You can use Visibility widget or just if to show and hide German text.
class selamlasmaLearn extends StatefulWidget {
#override
State<selamlasmaLearn> createState() => _selamlasmaLearnState();
}
class _selamlasmaLearnState extends State<selamlasmaLearn> {
bool _showGerman = false;
List<wordAndMeaning> wordsList = [
wordAndMeaning("Hello", "Hallo"),
wordAndMeaning("Go", "Gehen")
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (context) {
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(word.word,
style:
TextStyle(fontSize: 45, color: Colors.white)),
if (_showGerman) Text(word.meaning), //modify the way you want
],
),
const SizedBox(
width: 10,
),
IconButton(
icon: Icon(Icons.remove_red_eye_sharp),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
_showGerman = !_showGerman;
});
},
),
],
),
);
},
);
}).toList(),
);
}),
);
}
}
Use the Tooltip widget
I'm emphasizing on the popup part in your question title. When using a Tooltip you ensure that your widgets do not shift position or jump when the Tooltip widget appear, as the example below illustrates.
Example code:
import 'package:flutter/material.dart';
class TooltipExample extends StatelessWidget {
const TooltipExample({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Tooltip(
// Set the tooltip to trigger on a single tap, tapping outside the
// widget will make the tooltip disappear.
triggerMode: TooltipTriggerMode.tap,
// The message shown when the tooltip appears.
message: "Tooltip showing!",
// Consider adjusting this to your needs.
showDuration: const Duration(days: 1),
// The widget that must be clicked to show the tooltip.
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text("Hello"),
SizedBox(
width: 8,
),
Icon(Icons.visibility),
],
),
),
),
const Padding(
padding: EdgeInsets.all(8.0),
child: Text("Cover me!"),
)
],
),
);
}
}
// Some code to run the above example, note the theme part that turns the
// tooltip white.
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
// Style the overall design of tooltips in the app in one place,
// or provide in each tooltip individually.
theme: ThemeData(
tooltipTheme: const TooltipThemeData(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(4),
),
),
textStyle: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
),
),
),
home: const Scaffold(
backgroundColor: Colors.amber,
body: TooltipExample(),
),
);
}
}
void main() => runApp(const App());
Here is how it looks:
Note that the Tooltip widget overlays whatever is below it. (instead of pushing it further down - like toggling the visibility of a normal widget in a row or column would have done)
I stumbled upon a layout issue that would be rather obvious in native Android, but I cannot make it work in Flutter.
Let's say I have a layout like this:
Scaffold with AppBar + BottomNavBar + Column + Text
And now I want to fill the remaining white space with 2 Widgets (Containers in that case, but could be anything), to make it look like this (stretch and take all the remaining space): Column with remaining space filled
I tried placing Flex inside the Column and wrapping Containers in Flexible/Expanded with flex. But even after wrapping the Column in SizedBox I still get the constraints error:
The following assertion was thrown during performLayout():
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
Any idea how to achieve that? My code for this screen looks like this currently:
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Playground")),
bottomNavigationBar: Container(
width: double.infinity,
height: 70,
color: Colors.blueGrey,
),
body: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 20, left: 20),
child: Text("I am some text"),
),
Padding(
padding: const EdgeInsets.only(top: 20, left: 20),
child: Text("I am some text too"),
),
FlexibleContent(),
],
),
),
);
}
}
class FlexibleContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedBox(
child: Flex(
direction: Axis.vertical,
children: [
Expanded(flex: 7, child: Container(width: double.infinity, color: Colors.lightGreen)),
Expanded(flex: 3, child: Container(width: double.infinity, color: Colors.amberAccent)),
],
),
);
}
}
your column has an ubounded amount of height, so you have wrap it's child in a expanded/flexible widget. You can have a column with 3 children, 2 of the children that need to be the exact same size could be in a row, and it's children could be wrapped in a expanded/flexible widget.
Here's what I did:
import 'package:flutter/material.dart';
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Playground")),
bottomNavigationBar: Container(
width: double.infinity,
height: 70,
color: Colors.blueGrey,
),
body: Container(
color: Colors.red,
width: double.infinity,
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Padding(
padding: EdgeInsets.only(top: 20, left: 20),
child: Text("I am some text"),
),
Padding(
padding: EdgeInsets.only(top: 20, left: 20),
child: Text("I am some text too"),
),
Flexible( // sorcery
child: FlexibleContent(),
),
],
),
),
);
}
}
class FlexibleContent extends StatelessWidget {
const FlexibleContent({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Container(
width: double.infinity,
color: Colors.lightGreen,
),
),
Expanded(
child: Container(
width: double.infinity,
color: Colors.amberAccent,
),
),
],
);
}
}
If you add anything in the Container it should automatically expand to accommodate the contents. The Flexible widget well, makes the other Column of widgets, flexible.
My issue is what the title says - I have a gridview of flip cards. When I flip a few over, scroll past them and then scroll back up the cards have flipped back. I don't really want that to happen because it's supposed to be that every time the user flips a card a point is added to a total, and then when they flip it back a point is taken away from the total. I've tried "AutomaticKeepAliveClientMixin", which works to preserve the state when I have another tab, but it doesn't seem to help keep the cards flipped when I scroll.
Apologies in advance if an obvious solution exists here and I've missed it. I did try to find a solution online.
Here is the code from the dart file I'm working with:
import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'statenames.dart';
import 'globalVariables.dart';
StateNames stateObject = new StateNames();
class GridOne extends StatefulWidget {
final Function updateCounter;
final Function decreaseCount;
GridOne(this.updateCounter, this.decreaseCount);
#override
_GridOneState createState() => _GridOneState();
}
class _GridOneState extends State<GridOne>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
int points = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: GridView.count(
crossAxisCount: 4,
children: List.generate(52, (index){
return Card(
elevation: 0.0,
margin: EdgeInsets.only(left: 3.0, right: 3.0, top: 9.0, bottom: 0.0),
color: Color(0x00000000),
child: FlipCard(
direction: FlipDirection.HORIZONTAL,
speed: 1000,
onFlipDone: (status) {
setState(() {
(status)
? widget.decreaseCount()
: widget.updateCounter();
});
print(counter);
},
front: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FittedBox(fit:BoxFit.fitWidth,
child: Text(stateObject.stateNames[index], style: TextStyle(fontFamily: 'Architects Daughter', color: Colors.white), )
),
Text('',
style: Theme.of(context).textTheme.body1),
],
),
),
back: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage(stateObject.licensePlatePaths[index])),
],
),
),
),
);
})
),
);
}
}
Thank you so much for reading.
I did hit trial to achieve what you wanted and i think it's only possible if you use column. I even tried using simple ListView but still the the card will return to front face
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Test(),
);
}
}
class Test extends StatefulWidget{
#override
_Test createState() => _Test();
}
class _Test extends State<Test>{
List<Widget> list = [];
_Test(){
list = new List.filled(30, flipCards());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: list,
),
)
);
}
Widget flipCards(){
return Container(
height: 70,
child: FlipCard(
flipOnTouch: true,
front: Card(
child: Container(
alignment: Alignment.center,
child: Text('Toggle'),
),
),
back: Card(
child: Container(
alignment: Alignment.center,
child: Text('Back'),
),
),
)
);
}
}
You need to use the AutomaticKeepAliveClientMixin on the children of the GridView
So make a new stateful widget for your cards and use the keepalive there.
Also for the AutomaticKeepAliveClientMixin you need to call super.build(context); in your build method
I am facing a strange bug when I try to add a dynamic widget to my app. When press add button screen turns completely white I can not find why it happens.
I use https://www.youtube.com/watch?v=xPW1vtDDlt4 as resource I am really new at Flutter maybe I forget something to add bu I check many times.
Here is my code,
class DynamicWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: new TextField(
decoration: new InputDecoration(hintText: 'Press + to Add Field'),
),
);
}
}
Initialization of the list.
List<DynamicWidget> listDynamic = [];
My function to add widgets to the list.
addDynamic() {
listDynamic.add(new DynamicWidget());
print("addDynamic");
setState(() {});
}
I am not sure but problem might be here,
final testText = Visibility(
child: new Column(
children: <Widget>[
new Flexible(
child: new ListView.builder(
itemCount: listDynamic.length,
itemBuilder: (_, index) => listDynamic[index],
),
),
],
),
);
Here I call my widget which I declare it to variable here.
final body = Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
testText,
strPhoto
],
),
),
);
And finally my button.
return Scaffold(
appBar: new AppBar(title: new Text(device_type), centerTitle: true),
drawer: Menu(),
body: body,
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
addDynamic();
},
),
],
));
Thanks for helping me.
you must create variable Widget and add to Build Context on Scaffold
In the first step, i created a Widget called CustomTextField
import 'package:flutter/material.dart';
class CustomTextField extends StatelessWidget {
final String hint;
final TextEditingController controllers;
CustomTextField(this.hint, this.controllers);
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: hint),
controller: controllers),
SizedBox(height: 4.0),
],
),
);
}
}
this widget give me one text for display on hint and one controller for control textfield
And in the next step, I change the homepage class this way
i have list of Custom TextField (my widget) and display on listview using mapping list of build method
import 'package:flutter/material.dart';
import 'CustomTextField.dart';
class PageTutorial extends StatefulWidget {
#override
_PageTutorialState createState() => _PageTutorialState();
}
class _PageTutorialState extends State<PageTutorial> {
List<CustomTextField> widgets = [
CustomTextField("UserName", TextEditingController())
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: widgets.map<Widget>((widget) => widget).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widgets.add(CustomTextField("Password", TextEditingController()));
});
},
child: Icon(Icons.add)),
);
}
}
have fun