How to use Custom Widgets in flutter - flutter

I am new to Flutter and I am following Flutter's official tutorial to learn basics of flutter.
So, I want to create a reusable component named "CustomCard" and I did this:
class CustomCard extends StatelessWidget {
CustomCard({#required this.index, #required this.onPress});
final index;
final Function onPress;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
FlatButton(
child: const Text('Press'),
onPressed: this.onPress,
)
],
),
);
}
}
Now, to use it in MyApp, I did this:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to flutter',
home: Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: Center(
child: Text('centre'),
CustomCard(
index:'card1',
onPress: print(' this is $index')
),
),
),
);
}
}
Now my IDE says that:
The method 'CustonCard' isn't defined for the class 'MyApp'.
How to solve this?
Error in terminal:
Compiler message:
lib/main.dart:17:6: Error: Place positional arguments before named arguments.
Try moving the positional argument before the named arguments, or add a name to the argument.
CustomCard(
^^^^^^^^^^
lib/main.dart:17:6: Error: Expected named argument.
CustomCard(
^
lib/main.dart:15:21: Error: No named parameter with the name '#1'.
body: Center(
^^...
/C:/src/flutter/packages/flutter/lib/src/widgets/basic.dart:1863:9: Context: Found this candidate, but the arguments don't match.
const Center({ Key key, double widthFactor, double heightFactor, Widget child })
Edit: corrected a spelling mistake. Also adding console log.

You have got a few mistakes going on, but don't worry it happens at the very beginning. So let's step into the problems:
First, inside the body you've defined a Center Widget which only allows a single child within it but you have tried to put two Widgets (Text and CustomCard). So to put both widgets you could change it to something like this:
Center(
child: Column(
children: <Widget>[
Text('centre'),
CustomCard(...)
],
),
),
Moreover, pay attention that the onPress function takes a Function as argument but you are passing the result of print(...) which is void. Simply change it to:
CustomCard(index: index, onPress: () => print(' this is $index'))
Finally, I think you missed to define the index variable. Simply add:
String index = "card1";

Hey Please check the updated code here. There are couple of compile errors as you are wrapping up both Text and you CustomWidget in Center where it accepts only one child widget and also at the onPress method some code change required.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to flutter',
home: Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('centre'),
CustomCard(
index:'card1',
onPress: onPress
)
],
)
)
),
);
}
onPress(index){
print("this is $index");
}
}
class CustomCard extends StatelessWidget {
CustomCard({#required this.index, #required this.onPress});
final index;
final Function onPress;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
FlatButton(
child: const Text('Press'),
onPressed: (){
this.onPress(index);
},
)
],
),
);
}
}

child: Center(
child: Row(
children: <Widget>[
_box(), //Custom pin view
_box(), //Custom pin view
_box(), //Custom pin view
_box(),//Custom pin view
],
),
)
and Custom Widgets (_box) Code
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: 50,
width: 50,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}

Related

send data to another flutter screen

I need this screen to receive the name (winner X) of the winner that is sent from another screen how can I do this?
on the previous screen I ask the user to type the name and I made an if, if he wins his name appears, I tried with the push navgator and I couldn't
import 'package:flutter/material.dart';
class ganhadorScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Flutter Card Example')),
backgroundColor: Colors.yellow,
body: MyCardWidget(),
),
);
}
}
class MyCardWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
height: 500,
padding: const EdgeInsets.all(10.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: Colors.red,
elevation: 10,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
title: Text('PARABENS', style: TextStyle(fontSize: 30.0)),
),
Image.asset("assets/images/trofeu.png"),
Text('winner X',
style: TextStyle(fontSize: 18.0))
],
),
),
),
);
}
}
You can pass data as arguments to another class like this:
class MyClass extends StatelessWidget {
final String data;
final int number;
const MyClass({required this.data, required this.number});
// or like this if you don't like named arguments:
const MyClass(this.data, this.number);
#override
Widget build(BuildContext context) {
return Container();
}
}
So if the navigator pushes MyClass widget to the screen, you will have to write it like this:
MyClass(data: "some data", number: -1);
// or without named parameters, then like this:
MyClass("some data", -1); //note that without named parameters, the order of the parameters is important!
You can pass the values through the arguments property of navigation. In the navigation action of the previous page, however you do it,
Navigator.of(context).pushNamed('MyCardWidget', arguments: variable-that-holds-users-name);
In this screen you recieve the text
final name = ModalRoute.of(context)!.settings.arguments.toString();
Now you can use it
Text(name, style: TextStyle(fontSize 17)),

Drawer Works only with AppBar but does not with with Custom App Bar

I am trying to create a drawer..
I am working with customising every thing so I created a Custom Scaffold as the below code:
import 'package:flutter/material.dart';
import 'package:ipet/constants/constants.dart';
class IPetCustomScaffold extends StatelessWidget {
final Widget body;
final PreferredSizeWidget iPetTopAppBar;
final PreferredSizeWidget iPetBottomAppBar;
final Key ipKey;
final Color iPetBGScaffoldColor;
final Widget iPetDrawer;
const IPetCustomScaffold({
#required this.body,
this.ipKey,
#required this.iPetTopAppBar,
this.iPetBottomAppBar,
this.iPetBGScaffoldColor = AppConst.kPrimaryWhiteBgColor,
this.iPetDrawer,
});
#override
Scaffold build(BuildContext context) {
return Scaffold(
backgroundColor: iPetBGScaffoldColor,
key: ipKey,
appBar: iPetTopAppBar,
body: SafeArea(
child: body,
),
bottomNavigationBar: iPetBottomAppBar,
drawer: iPetDrawer,
);
}
}
and I have a custom AppBar as the below code:
import 'package:flutter/material.dart';
import 'package:ipet/constants/ipet_dimens.dart';
class IPetCustomTopBarWidget extends StatelessWidget
implements PreferredSizeWidget {
final String iPetPawImage;
final double iPetIconSize;
final IconData iPetListIconData;
final Widget iPetFirstPart;
final List<Widget> iPetMiddlePart;
final List<Widget> iPetLastPart;
#override
final Size preferredSize;
IPetCustomTopBarWidget({
this.iPetIconSize = IPetDimens.space20,
this.iPetListIconData,
this.iPetPawImage,
#required this.iPetFirstPart,
#required this.iPetMiddlePart,
this.iPetLastPart,
}) : preferredSize = Size.fromHeight(IPetDimens.space60);
ShapeBorder kBackButtonShape = RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(IPetDimens.space30),
),
);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: IPetDimens.space15),
child: iPetFirstPart,
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: iPetMiddlePart,
),
),
Row(
children: iPetLastPart,
),
],
),
);
}
}
Now I need to create a drawer in my DashBoard Page:
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return IPetCustomScaffold(
iPetTopAppBar: IPetCustomTopBarWidget(
iPetMiddlePart: [
DefaultImage(
image: 'assets/images/ipet_paw_img.png',
),
Label(
text: 'Settings',
),
],
iPetLastPart: [],
),
iPetDrawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough ver tical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
body: ...
);
the problem now the drawer does not working with this customised app bar works only with normal AppBar() as a default Widget and the result with the AppBar as the below image
and this is the result when using Custom App Bar
I may be working with wrong technique but I hope someone recommend a good advice :D
You can use this line wherever you want to open the drawer:
Scaffold.of(context).openDrawer();
Edit
when you are using a custom one, you have to assign a key to the Scaffold to differentiate which scaffold should open the Drawer
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
then after putting this Key in the Scaffold, call this whenver you want to open the drawer programmatically:
_scaffoldKey.currentState.openDrawer();

Button OnPressed is not working in Flutter

I have issue with the Raised button click. The method inside OnPressed() is not getting called. Ideally on the OnPressed() method i would like to have the pop up or a slider shown. I have created the example to show the problem currently faced.
The main.dart file calls Screen2()
import 'package:flutter/material.dart';
//import 'package:flutter_app/main1.dart';
import 'screen2.dart';
void main() => runApp(Lesson1());
class Lesson1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen2(),
);
}
}
and in Screen2()i have just have a RaisedButton and OnPressed() it needs to call the function ButtonPressed().
import 'package:flutter/material.dart';
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('Screen 2'),
),
body: Center(
child: RaisedButton(
color: Colors.blue,
child: Text('Go Back To Screen 1'),
onPressed: () {
print('centrebutton');
ButtonPressed();
},
),
),
);
}
}
class ButtonPressed extends StatefulWidget {
#override
_ButtonPressedState createState() => _ButtonPressedState();
}
class _ButtonPressedState extends State<ButtonPressed> {
#override
Widget build(BuildContext context) {
print ('inside button press');
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text('Screen 3'),
),
// create a popup to show some msg.
);
}
}
On clicking the Raised button the print statement ('centerbutton') gets printed.
But the ButtonPressed() method is not getting called .
I am not able to see the print msg('inside button press') in the console. Pl. let me what could be the reason for ButtonPressed method not getting called. Attached the snapshot for your reference.
You are calling a Widget on your RaisedButton's onPressed method. Your widget is getting called but will not render anywhere in the screen.
You should call a function for processing your data in a tap event. But you are calling a widget or say a UI view.
If you want to navigate to the respective screen then you should use navigator.
For ex :
onPressed: () {
print('centrebutton');
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => ButtonPressed()));
},
This could be a cause: If you have an asset as a child of your button, and that asset does not exist, the button onpressed will not work.
Solution: remove the asset.
Example:
return RaisedButton(
splashColor: Colors.grey,
onPressed: () {
},
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage("assets/google_logo.png"), height: 35.0), //remove this line
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 20,
color: Colors.grey,
),
),
)
],
),
),
);

Flutter Scaffold.of(context).openDrawer() doesn't work

I want to open a drawer after pushing on the custom button in BottomMenu I have trouble with Scaffold.of(context).openDrawer(), it doesn't work. My BottomMenu is a separate widget class. As I understand, it doesn't work because it's a separate context. How can I get the right context? Or perhaps someone knows another solution.
Here my code reproducer:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Drawer'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: BottomMenu(),
endDrawer: SizedBox(
width: double.infinity,
child: Drawer(
elevation: 16,
child: Container(
color: Colors.black,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
],
),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Call Drawer form menu reproducer',
)
],
),
),
);
}
}
class BottomMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Wrap(
alignment: WrapAlignment.center,
children: <Widget>[
Divider(color: Colors.black, height: 1),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
borderRadius: new BorderRadius.circular(20.0),
customBorder: Border.all(color: Colors.black),
child: Container(
padding: EdgeInsets.only(
left: 3, right: 6, bottom: 15, top: 11),
child: Row(
children: <Widget>[
Icon(Icons.menu),
Text('Show menu', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
],
),
),
onTap: () {
Scaffold.of(context).openDrawer();
},
),
],
),
),
],
),
);
}
}
In my case, this worked.
return Scaffold(
key: _scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This!
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
leading: IconButton(
icon: Icon(Icons.menu, size: 36),
onPressed: () => _scaffoldKey.currentState.openDrawer(), // And this!
),
),
drawer: DrawerHome(),
....
and _scaffoldKey must be initialized as,
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
under the class.
The problem is that you specified endDrawer on Scaffold yet you're calling Scaffold.of(context).openDrawer().
openDrawer() documentation states:
If the scaffold has a non-null Scaffold.drawer, this function will cause the drawer to begin its entrance animation.
Since your drawer is null, nothing happens.
In contrast, openEndDrawer() informs us:
If the scaffold has a non-null Scaffold.endDrawer, this function will cause the end side drawer to begin its entrance animation.
Since your endDrawer is not null you should use openEndDrawer() method. Alternatively, if you don't care which side the drawer slides in from, you can use drawer instead of endDrawer when building Scaffold.
My problem solved that instead of
Scaffold.of(context).openEndDrawer()
I give key to Scaffold and then I call by state like below
_scaffoldkey.currentState.openEndDrawer()
It solved my problem I hope It also works for you
Scaffold.of(context).openEndDrawer()
The Problem
This issue can occur when you do not use the correct BuildContext when calling Scaffold.of(context).openDrawer() (or openEndDrawer()).
Easiest Solution
Simply wrap whatever calls openDrawer() (or openEndDrawer()) with a Builder widget. This will give it a working context.
Minimal Working Example
// your build method
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Builder(builder: (context) { // this uses the new context to open the drawer properly provided by the Builder
return FloatingActionButton(onPressed: (() => Scaffold.of(context).openDrawer()));
}),
drawer: const Drawer(
child: Text("MY DRAWER"),
),
);
}
Similar problem here. Clicked on button and nothing happened. The problem is I was using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.
Here is how I solved it:
// body: Column(
// children: <Widget>[
// Row(
// children: <Widget>[
// IconButton(
// icon: Icon(Icons.filter_list),
// onPressed: () => Scaffold.of(context).openEndDrawer(), (wrong context)
// ),
// ],
// ),
// ],
// )
To:
body: Builder(
builder: (context) => Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () => Scaffold.of(context).openEndDrawer(),
),
],
),
],
)),
),
Assign Drawer to drawer property in scaffold. Wrap your specific Widget/Button(where you want to open drawer on its click method) with Builder. Use below method on click property:
enter image description here
Scaffold.of(context).openDrawer();
If you have the appbar widget with an action button to launch the drawer and the drawer is never pushed please remember that you need to define after appbar: ... the endDrawer: YOURAppDrawerWIDGET(), or else using the Scaffold.of(context).openEndDrawer() will not work.
Scaffold(
appBar: AppBar(title: Text(_title)),
endDrawer: AppDrawer(), // <-- this is required or else it will not know what is opening
body: SingleChildScrollView(
///...

onPressed call not defined

Working on an App that will require multiple screens. The below right now shows only two icons, more later, and i need them the be able to go the a corresponding screen when pressed. Everything works but the onPressed function. The error I get is
The named parameter "onPressed" is not defined
Do I have the onPressed function in the wrong spot? I have tried moving it between other functions but I get the same error.
Any help is appreciated
main.dart
import 'package:flutter/material.dart';
import './food_screen.dart';
void main(List<String> args) {
runApp(new MaterialApp(
home : MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title :Text('Main Title'),
backgroundColor: Colors.blue,
),
backgroundColor: Colors.blue[100],
body: Container(
padding: EdgeInsets.all(30.0),
child: GridView.count(
crossAxisCount: 2,
children: <Widget>[
Card(
margin: EdgeInsets.all(8.0),
child: InkWell(
onTap: (){
Navigator.push(context,
MaterialPageRoute(builder: (context)=>FoodScreen())
);
},
splashColor: Colors.blue,
child: Center(
child: Column(
children: <Widget>[
Icon(Icons.fastfood, size: 70.0),
Text("FOOD", style: new TextStyle(fontSize: 28.0))
]
)
),
),
),
Card(
margin: EdgeInsets.all(8.0),
child: InkWell(
onTap: (){},
splashColor: Colors.blue,
child: Center(
child: Column(
children: <Widget>[
Icon(Icons.directions_car, size: 70.0),
Text("VEHILCES", style: new TextStyle(fontSize: 28.0))
],
),
),
),
),
]
)
)
);
}
}
food_screen.dart
import 'package:flutter/material.dart';
import './main.dart';
class FoodScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
);
}
}
Card doesn't support onPressed property, you already have InkWell which has onTap, you can put onPressed method action inside it.
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context)=>FoodScreen())
);
}
Card doesn't have any property of onpressed()
you can add a floating button and Route it to the the second page i.e food_screen.dart
https://api.flutter.dev/flutter/material/Card-class.html
if you want to add a tap on Card Widget just wrap the card with GestureDetector.