How to make interactive icon in flutter? - flutter

I have a small problem.
I want to click on my custom icon, and I don't know how to do that :c
after this click, I want to go to another widget, but with that, I can try to work (i hope)
if u can tell me also how to do it with the entire widget.
class secondWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
margin: EdgeInsets.only(top: 100, bottom: 10),
color: Colors.pink,
child: new Icon(const IconData(0xe800, fontFamily: 'Dupa')),
//this is not working :c
onPressed: () {
newWidgetGoBrr();
});
);

Container doesn't have an onPressed. You need to use a GestureDetector widget with either Container or Icon.
GestureDetector(
onTap: () {
// after click logic goes here.
newWidgetGoBrr();
},
child: Container(
height: 100,
width: 100,
margin: EdgeInsets.only(top: 100, bottom: 10),
color: Colors.pink,
child: new Icon(const IconData(0xe800, fontFamily: 'Dupa')),
),
),

What you need is some type of detector that can detect your gesture. It can be obtained through many widgets but the most frequently used one is GestureDetector and InkWell.
You can wrap your Icon with an either of the widget. These widgets gives you access to the onTap property.
It's better to wrap your Icon with the Inkwell/GestureDector than to wrap the whole container(that depends on your requirement).
You can implement it like this:
class secondWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
margin: EdgeInsets.only(top: 100, bottom: 10),
color: Colors.pink,
child: InkWell(
onTap: newWidgetGoBrr(), // your logic
child: Icon(
const IconData(0xe800, fontFamily: 'Dupa'),
),
),
);
}
}
Note: With the new flutter release you don't need to use the new keyword anymore.

Related

I want to use remaining available space on my page in Flutter

In the screen, I have a Column, it has a cusotm made widget of specific height. Then, I have Expanded, in which I have a TabBar which has three tabs.
In one of those tabs, I want to show a list. First, I have a padding, which contains column. The column has some text, which should remain at top and the list should be shown in the space which is remaining. I am using Expanded for that, but it is not working.
I can't use ListView directly, and also can't use expanded. It is only working when I am giving it a container of fix size. Now, in different screens, it will look different. So, I want to take all of the remaining space and build the list there. Code for reference -
Here is the doubts screen, which is one of the tabs of main screen -
import 'package:flutter/material.dart';
import 'package:my_board_plus/size_config.dart';
import 'package:my_board_plus/styles.dart';
import '../../api_handling/api_fetch/fetch_doubt_questions.dart';
import '../../data_models/doubt_question_model.dart';
class NewDoubtsScreen extends StatefulWidget {
const NewDoubtsScreen({Key? key}) : super(key: key);
#override
State<NewDoubtsScreen> createState() => _NewDoubtsScreenState();
}
class _NewDoubtsScreenState extends State<NewDoubtsScreen> {
late Future<List<DoubtQuestionModel>> doubtQuestionsList;
#override
void initState() {
doubtQuestionsList = fetchDoubtQuestion();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor2,
floatingActionButton: Container(
width: getProportionateScreenWidth(130),
height: getProportionateScreenHeight(50),
decoration: BoxDecoration(
color: brandPurple,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: Text(
'? My Doubts',
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
),
),
body: Padding(
padding: EdgeInsets.only(top: 8.0, left: 5),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Trending Doubts',
style: TextStyle(
color: Colors.white,
),
),
Text(
'View all',
style: TextStyle(
color: brandYellow,
decoration: TextDecoration.underline
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
height: getProportionateScreenHeight(530),
width: double.infinity,
color: Colors.red,
),
),
],
),
),
);
}
}
The red area that you are seeing is the one. I want it to occupy whole area available in the phone screen, so I can show list in it which should be scrollable. In this case, it is occupying all, but in different screens, it might not. So, please give me some suggestions.
You can try to give the height of the container like
height: double.infinity
Or you can give the height of it with substracting the other height from the screen size like
height: MediaQuery.of(context).size.height - getProportionateScreenHeight(50) //the heigth size that you give the other widget that top of it
try to wrap your Padding widget with the Expanded widget,
Expanded widget in column will take the rest of the space of the screen.
also no need to give height to Container widget, so you can remove getProportionateScreenHeight(530) this one

Why does my widget rebuild when I use keyboard

I have this issue of rebuilding widget when The keyboards shows up. I tried to use the sizer package but never could figure out how to get it to work
when I go back from this screen everything in the previous screen will rebuild, Please note: If I don't click on the typeaheadwidget such that the keyboard doesn't show up the state is preserved in the previous screen but as soon as the keyboard pops up the widgets get rebuilt
Could you please check ?
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
TextEditingController pickUpTextEditingController = TextEditingController();
TextEditingController dropOffTextEditingController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
#mustCallSuper
Widget build(BuildContext context) {
String placeAddress =
Provider.of<AppData>(context).pickUpLocation.placeName ?? "";
pickUpTextEditingController.text = placeAddress;
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
Container(
height: 250.0,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 6.0,
spreadRadius: 0.5,
offset: Offset(0.7, 0.7),
)
],
),
child: Padding(
padding: EdgeInsets.only(
left: 25.0, top: 30.0, right: 25.0, bottom: 20.0),
child: Column(
children: [
SizedBox(height: 5.0),
Stack(
children: [
GestureDetector(
onTap: () {
Navigator.pop(
//send back data
context,
dropOffTextEditingController.text);
},
child: Icon(Icons.arrow_back)),
Center(
child: Text(
"Set Drop Off",
style: TextStyle(
fontSize: 18.0, fontFamily: "Brand-Bold"),
),
)
],
),
SizedBox(height: 16.0),
Row(
children: [
Image.asset("images/images/pickicon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TextField(
controller: pickUpTextEditingController,
decoration: InputDecoration(
hintText: "PickUp Location",
fillColor: Colors.grey[400],
filled: true,
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.only(
left: 11.0, top: 8.0, bottom: 8.0),
),
),
),
))
],
),
SizedBox(height: 10.0),
Row(
children: [
Image.asset("images/images/desticon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TypeAheadField(
itemBuilder: null,
onSuggestionSelected: null,
suggestionsCallback: null,
),
),
),
),
],
),
],
),
),
),
],
),
);
}
}
You should not try to control when the build method is called. Flutter will call build when it decides it needs to (e.g. keyboard appears, device rotated, parent rebuilds, etc).
Instead, you should make sure that your build method is a "pure" function. In Flutter specifically, this means that you should not perform any action with "side-effects" (basically anything which modifies the state of the app).
For example:
Widget build(BuildContext context) {
final x = 2 + 3; // fine, nothing else is modified
final state = context.watch<MyModel>(); // also fine, only reading data
controller.text = "hello"; // BAD, modifies the state of the app
return ...;
}
Instead, you should move your logic with side effects into other lifecycle methods (e.g. initState(), didChangeDepencencies(), etc).
For example, if you want to set your text field to a particular string when it first appears, you can use initState:
class _SearchScreenState extends State<SearchScreen> {
#override
void initState() {
super.initState();
final data = context.read<AppData>();
controller.text = data.pickUpLocation.placeName ?? "";
}
Widget build(BuildContext context) {
// ...
}
}
Now build() can be called whenever it has to be, without resetting the state of your text field.
Note that, even if there was some way to prevent your widget from being rebuilt, this is also likely not what you want, since the UI would not update to accommodate the keyboard.
the only reason why your widgets got rebuilds after keyboard pop up.
is that one or more of your widgets size depends on MediaQuery.
you can try to ge your screen size from LayoutBuilder as an alternative for MediaQuery.
Give the textfield an initial value like this:
initvalue:Provider.of<AppData>(context).pickUpLocation.placeName ?? ""
and use onchange method in text field instead of text editing controller like this:
onchange(value){
Provider.of<AppData>(context).pickUpLocation.placeName=value;}
I am also facing the same issue, My blocBuidler is getting rebuilt every time when click on textfield or keyboard is appear.
In my case, I was calling the event in parent BlocBuilder so whenever I pressed on textfields the parent BlocBuilder is called the event, so it builds state of child BlocBuilder
Make sure you are also doing the same thing. If you are doing the same thing please check the state whether it is already built or not.
(BlocProvider.of<YourBlocName>(context).state is YouBlocState) ? Print('do nothing'): BlocProvider.of<YourBlocName>(context).add(youBlocEvent);
When you tap the TextField widget, it makes the keyboard show up. And when the keyboard shows up, your screen size changes. This causes the rebuild

Fixed height in Container is not working in Flutter

Container height is set to fixed 40 but once I'm using that Widget in AppBar() it takes all the possible height. Here is the code for my custom widget which has Fixed height of Container,
class LPBorderButtonWithIcon extends StatelessWidget {
final GestureTapCallback onPressed;
final String text;
final String iconAsset;
final Color textColor;
LPBorderButtonWithIcon(
{#required this.onPressed,
#required this.text,
#required this.textColor,
#required this.iconAsset});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Color(0XFFd8dce1))),
child: Row(
children: [
WidthSizedBox(15),
Image.asset(
iconAsset,
height: 14,
width: 14,
),
WidthSizedBox(5),
Text(text,
style: TextStyle(
color: textColor,
fontSize: 12,
fontFamily: "GilroyMedium")),
WidthSizedBox(15),
],
),
));
}
}
and here I'm using LPBorderButtonWithIcon() in this screen,
class CreateRulesScreen extends StatefulWidget {
#override
_CreateRulesScreenState createState() => _CreateRulesScreenState();
}
class _CreateRulesScreenState extends State<CreateRulesScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
brightness: Brightness.light,
backgroundColor: Colors.white,
elevation: 1,
centerTitle: false,
titleSpacing: 0.0,
leading: BackButton(
color: LPColor.primary,
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Create Rule",
style: LPStyle.titleStyle,
),
actions: [
Container(
margin: EdgeInsets.only(top: 12, bottom: 12, right: 16),
child: LPBorderButtonWithIcon(
onPressed: null,
text: "Create",
textColor: Color(0XFF508ff4),
iconAsset: "images/ic_publish.png",
),
)
],
),
);
}
}
and below is the result where that custom container takes all the possible height. Please let me know how can I set fixed height to my custom widget.
Place your Container inside an Align, Aling will force the container to occupy only the space it needs.
Align(
child: Container(
height: 20,
width: 30,
color: Colors.white,
),
)
The parent widget takes the entire space available to draw the widget, Here Container is the parent widget, and it's taking whatever space is available, so to give height to the Container, that needed to be placed inside any widget which assigns x,y position of widgets to get it to draw.
Container(
height: 40, // Its not going to apply height as it's parent widget
)
So to work out the above code you have to align Container to any other widget like Center, Align, etc.
For Eg:
Scaffold(
body: Container(
height: 600,
color: Colors.red,
child: Container(
height: 200,
color: Colors.yellow,
),
),
);
The above example child container will not draw yellow color in 200 height, it will take the entire 600 height space.
Output:
To Solve this we have assigned some widgets to the child Container so that it will get the x, y position to start drawing the child widget. Here Center widget is used.
Eg:
Scaffold(
body: Container(
height: 600,
color: Colors.red,
child: Center(
child: Container(
height: 200,
color: Colors.yellow,
),
),
),
);
Output:
Some Limitation:
A widget can decide its own size only within the constraints given to
it by its parent. This means a widget usually can’t have any size it
wants.
A widget can’t know and doesn’t decide its own position in the
screen, since it’s the widget’s parent who decides the position of
the widget.
Since the parent’s size and position, in its turn, also depends on
its own parent, it’s impossible to precisely define the size and
position of any widget without taking into consideration the tree as
a whole.
If a child wants a different size from its parent and the parent
doesn’t have enough information to align it, then the child’s size
might be ignored. Be specific when defining alignment.
Reference link: https://flutter.dev/docs/development/ui/layout/constraints

How to access the widget or the text of the button that was just clicked?

I would like to add the button that was clicked to a list but i have no idea how to access the clicked button itself.
This is my current list of Buttons.
Created through:
for (var stand in stands) StandCreation(stand)
This is the list i want to fill:
List<String||Widget> SelectedStandList = []
This is how i create each 'Button':
import 'package:flutter/material.dart';
class StandCreation extends StatelessWidget {
final String stand;
StandCreation(this.stand);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {}, // Here i would like to do something like SelectedStandList.add(tappedInkWell)
child: Container(
width: double.infinity,
margin: const EdgeInsets.only(
top: 5,
bottom: 5,
),
padding: const EdgeInsets.all(3.0),
color: Colors.blue,
child: Text(
stand,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 23,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
With the list i want then to create a new screen of only the selected items.
Appreciate any help!
Use ListView.builder instead of For loop. change your StandCreation to this:
final VoidCallback onPressed;
StandCreation({this.stand, this.onPressed});
and change onTap to this:
onTap: onPressed
and finally change your For loop to this:
ListView.builder(
itemCount: 6, // how many buttons you want
itemBuilder: (context, index) {
return StandCreation(stand: stand,
onPressed:(){
// Here is your onclick, do whatever you want with it
}
)
},
)
If you want each of your onPressed functions have different actions then you can simple put an If statement in there:
onPressed:(){
if(index==0) //do something with first button
}

Animating Widget to a position outside of it's parent (Row/Listview)

I have a list of card widgets inside a row or listview. When clicking on of these cards i want to create an effect where the card grows and moves to the middle of the screen, and the rest of the screen appears below a grey overlay.
These example images should help you understand what i'm trying to explain.
Image #1 - List before Clicking in any card
Image #2 - After clicking a card. The card animates in size and position to the center of the screen. Everything else gets becomes dark. (Ignore bad Photoshop).
I'm not asking for full code or anything, just want to know if it's possible to move a widget outside it's parent and get some ideas of how to achieve this effect. I know AnimatedContainer can be used on the card to make it grow, the positioning part is what need help with. Thanks!
You can use the transform: argument on a Container()
Full Working Example
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
color: Colors.black87,
margin: EdgeInsets.only(top: 100),
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
transform: Matrix4.translationValues(0, 50, 0),
color: Colors.red,
height: 100,
width: 100,
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
color: Colors.red,
height: 100,
width: 100,
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
color: Colors.red,
height: 100,
width: 100,
),
],
),
)
)
);
}
}
The #2 Animation can be accomplished by using a Hero() Widget and Overlay(). Or you can use a custom DialogBuilder just a few Suggestions to get you started.