import 'package:flutter/material.dart';
import 'package:flutter_bmi_app/second_screen.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class BmiCalc extends StatefulWidget {
const BmiCalc({Key? key}) : super(key: key);
#override
State<BmiCalc> createState() => _BmiCalcState();
}
class _BmiCalcState extends State<BmiCalc> {
Color colorOfLittleBox = Color.fromARGB(255, 27, 28, 48);
Color colorOfLittleBox2 = Colors.pink;
bool isMale = true;
double _value = 150;
int weight = 60;
int age = 25;
double answer = 10;
String calc = "CALCULATE";
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 12, 9, 34),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Row(
children: [
FemaleBox("MALE", Icons.male),
FemaleBox("FEMALE", Icons.female),
],
),
Column(children: [
Container(
padding: EdgeInsets.all(32),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color.fromARGB(255, 27, 28, 48),
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("HEIGHT",
style: TextStyle(color: Colors.grey, fontSize: 20)),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_value.toStringAsFixed(0),
style: const TextStyle(
fontSize: 45,
color: Colors.white,
fontWeight: FontWeight.w900)),
const Text(
"cm",
style:
TextStyle(fontSize: 20, color: Colors.grey),
),
],
),
Slider(
min: 100,
max: 230,
thumbColor: Colors.pink,
value: _value,
onChanged: (value) {
setState(() {
_value = value;
});
},
),
],
))
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Operation("Weight"),
Operation("Age"),
],
),
Container(
decoration: BoxDecoration(
color: Colors.pink,
borderRadius: BorderRadius.circular(15),
),
padding: EdgeInsets.only(bottom: 5),
width: MediaQuery.of(context).size.width,
child: TextButton(
child: Text(
calc,
style: const TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.w900),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
),
)
],
),
),
),
);
}
void calculate() {
answer = (weight / (_value * _value)) * 10000;
Text(answer.toString(),
style: const TextStyle(fontSize: 40, color: Colors.white));
if (calc == "CALCULATE") {
calc = answer.toStringAsFixed(1);
} else {
calc = "CALCULATE";
}
setState(() {});
}
}
I made bmi calculator, I wanna have answer on other screen. I want to send this function calculate() to the second screen, where I will
have the answer of this calculation. I gave Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()), but how to make it work? Thank you in advance.
Make the SecondScreen constructor take a parameter for the type of data that you want to send to it.
const SecondScreen(
{Key? key,required this.answer, })
: super(key: key);
final String? answer; //define value you want to pass
#override
_SecondScreenScreenState createState() => _SecondScreenState();
}
And pass data when navigate
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(answer: 'Hello',),
));
here is the example:
import 'package:flutter/material.dart';
class Todo {
final String title;
final String description;
const Todo(this.title, this.description);
}
void main() {
runApp(
MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
),
);
}
class TodosScreen extends StatelessWidget {
const TodosScreen({Key? key, required this.todos}) : super(key: key);
final List<Todo> todos;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
// When a user taps the ListTile, navigate to the DetailScreen.
// Notice that you're not only creating a DetailScreen, you're
// also passing the current todo through to it.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
);
}
}
class DetailScreen extends StatelessWidget {
// In the constructor, require a Todo.
const DetailScreen({Key? key, required this.todo}) : super(key: key);
// Declare a field that holds the Todo.
final Todo todo;
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
I can't get the FemaleBox and Operation in your project so I can't run that try the above example or share you full code include second screen also
add a constructor in your second screen and pass it while calling second screen
const SecondScreen(
{Key? key,required this.answer, })
: super(key: key);
final String? answer; //define value you want to pass
#override
_SecondScreenScreenState createState() => _SecondScreenState();
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(answer: 'Hello',),
));
There is another way to do that.
Create a class as given below and use static keyword to define any variable.
Now you can call this variable at your entire app via- Common.sharedData
So you can modified it according to you
Class Common{
static int sharedData=0;
//Other function
}
There are different ways to solve this
Sending parameters through constructor (Good solution).
Use a State Management package and hold the state in its class and access tit everywhere (Recommended way).
declare variable globally and use it anywhere in the app (not Recommended)
Related
I am trying to use OverlayEntry on onChanged callback from TextField.
body: Center(
child: Container(
child: TextField(
focusNode: _focusNode,
key: _textFieldKey,
style: _textFieldStyle,
onChanged: (String nextText) {
showOverlaidTag(context, nextText);
},
),
width: 400.0,
),
)
The problem is when everytime onChanged is triggered, new Overlay is created and lay on each other? I want to just new text replaced with new text and the old overlay remains?
showOverlaidTag(BuildContext context, String newText) async {
OverlayEntry suggestionTagoverlayEntry = OverlayEntry(
builder: (BuildContext context) => DropDownBody(
focusNode: _focusNode,
newText : newText
),
);
overlayState.insert(suggestionTagoverlayEntry);
//await Future.delayed(Duration(milliseconds: 10000));
suggestionTagoverlayEntry.remove();
}
This is DropDownBody
import 'package:flutter/material.dart';
class DropDownBody extends StatefulWidget {
const DropDownBody({Key? key, required this.focusNode, required this.newText})
: super(key: key);
final FocusNode focusNode;
final String newText;
#override
_DropDownBodyState createState() => _DropDownBodyState();
}
class _DropDownBodyState extends State<DropDownBody> {
#override
Widget build(BuildContext context) {
print("=====> build");
return Positioned(
top: widget.focusNode.offset.dy + 50,
left: 0,
child: Material(
elevation: 4.0,
color: Colors.transparent,
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
color: Colors.lightBlueAccent,
child: Row(
children: [
Expanded(
child: Text(
'Show tag here',
style: TextStyle(
fontSize: 20.0,
),
),
),
],
),
)),
);
}
}
I had to fix a few syntax errors in your code. I implemented the dropdownbody code in part. works great.
class HomePageWidget extends StatefulWidget {
HomePageWidget({Key? key}) : super(key: key);
final formKey=GlobalKey<FormState>();
TextEditingController text_controller= TextEditingController();
#override
_HomePageWidgetState createState() => _HomePageWidgetState();
}
class _HomePageWidgetState extends State<HomePageWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text("test changenotify"),
),
body: Form( key:widget.formKey, child: Column(
children: [
TextFormField(
decoration: InputDecoration(icon: const Icon(Icons.person),
hintText: "Input some text",
labelText: "Input",
),
onChanged: (String nextText){
//debugPrint(nextText);
showOverlaidTag(context,nextText);
//Future<bool> val=showOverlaidTag(context,nextText);
//val.then((result)=>result);
},
controller: widget.text_controller),
],
)));
}
showOverlaidTag(BuildContext context, String newText) async {
OverlayEntry suggestionTagoverlayEntry = OverlayEntry(
builder: (BuildContext context) =>
Positioned(
top:100,
child:
Material(elevation:4.0,
color:Colors.transparent,
child:Container(
width:MediaQuery.of(context).size.width*0.9,
color:Colors.lightBlueAccent,
child:
Row(children: [Expanded(child: Text(
//focusNode: _focusNode,
newText
))],)
),
)));
//overlayState.insert(suggestionTagoverlayEntry);
Overlay.of(context)?.insert(suggestionTagoverlayEntry);
await Future.delayed(Duration(milliseconds: 2000));
suggestionTagoverlayEntry.remove();
return suggestionTagoverlayEntry;
}
}
use boolean and try to call the insert only the first time
if(isOverlayAlreadyRunning == false){
entry = OverlayEntry(builder: (context) {
return Positioned(
);
});
overlay?.insert(entry!);
isOverlayAlreadyRunning = true;
}
and use setState((){}) to update the variable
try this, did a little refactoring for the text, it doesn't change much. Call hideOverlay() when you want to remove it
import 'package:flutter/material.dart';
class DropDownBody extends StatefulWidget {
const DropDownBody({Key? key, required this.focusNode, required this.newText})
: super(key: key);
final FocusNode focusNode;
final String newText;
#override
_DropDownBodyState createState() => _DropDownBodyState();
}
class _DropDownBodyState extends State<DropDownBody> {
OverlayEntry? overlayEntry;
String? text;
void insertOverlay(BuildContext context) {
if (overlayEntry == null || !overlayEntry!.mounted) {
overlayEntry = OverlayEntry(
builder: (context) => DropDownBody(focusNode: _focusNode, newText: text??'sus')
);
final overlay = Overlay.of(context)!;
overlay.insert(overlayEntry!);
}
}
void hideOverlay() {
overlayEntry!.remove();
overlayEntry == null;
}
#override
Widget build(BuildContext context) {
print("=====> build");
return Positioned(
top: widget.focusNode.offset.dy + 50,
left: 0,
child: Material(
elevation: 4.0,
color: Colors.transparent,
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
color: Colors.lightBlueAccent,
child: Row(
children: [
Expanded(
child: Text(
'Show tag here',
style: TextStyle(
fontSize: 20.0,
),
),
),
],
),
)),
);
}
}
New to flutter and coding so newbie warning :D, I'm trying to create a gridview.builder that will on tap redirect to correctly assigned to that index page (that is already created) but I don't know how to assign it in model.dart and then to pass it to Navigator in InkWell, would greatly appreciate any explanation how this works
this is gridview page
import 'package:flutter/material.dart';
import 'package:practice/data/model.dart';
import 'package:practice/builds/allbuilds.dart';
class GridViewPage extends StatefulWidget {
#override
_GridViewPage createState() => _GridViewPage();
}
class _GridViewPage extends State<GridViewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey,
automaticallyImplyLeading: true,
title: const Text(
'Builds',
style: TextStyle(
fontFamily: 'Lexend Doca',
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
actions: const [],
centerTitle: false,
elevation: 2,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(15, 0, 15, 25),
child: GridView.builder(
itemCount: griddata.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1,
),
itemBuilder: (context, index) {
return GridSingleItem(itemGriddata: griddata[index]);
},
),
),
),
],
));
}
}
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
const GridSingleItem({Key key, #required this.itemGriddata})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => PageX()));
},
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: const Color(0x00EEEEEE),
image: DecorationImage(
fit: BoxFit.cover,
image: Image.asset(itemGriddata["image"]).image,
),
),
),
),
);
}
}
and this is model
const griddata = [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
}
];
If I got your question right.
You want to create a model for a particular data,
and then use the data in gridviewbuilder,
and then pass the data to the next screen/page when tapped. [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
}
]
from the data we can have
class Data{
final String name;
final String image;
Data({this.name,this.image});
}
Now const griddata becomes;
const griddata = [
Data(name:'Page1',image:"assets/images/Image1.png",),
Data(name: "Page2","image : "assets/images/Image2.png")
];
What we've done so far is create a model from the raw gridData,
Now passing the data to other pages;
I'll recommend making the destination page i.e PageX to accept Data model
like this;
class PageX extends StatelessWidget{
final Data data; //<------
PageX(data);
#override
Widget build(){
....
}
}
Then in your onTap property, you can have someting like;
Navigator.push(
context, MaterialPageRoute(builder: (context) => PageX(data)));
UPDATE:
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
final Function onTap; //add a function as a parameter/property here
const GridSingleItem({Key key,
#required this.itemGriddata,
this.onTap,
this.image,
})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap:onTap,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: const Color(0x00EEEEEE),
image: DecorationImage(
fit: BoxFit.cover,
image: Image.asset(itemGriddata["image"]).image,
),
),
),
),
);
}
}
FULL CODE :
import 'package:flutter/material.dart';
import 'package:practice/data/model.dart';
import 'package:practice/builds/allbuilds.dart';
class GridViewPage extends StatefulWidget {
#override
_GridViewPage createState() => _GridViewPage();
}
class _GridViewPage extends State<GridViewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey,
automaticallyImplyLeading: true,
title: const Text(
'Builds',
style: TextStyle(
fontFamily: 'Lexend Doca',
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
actions: const [],
centerTitle: false,
elevation: 2,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(15, 0, 15, 25),
child: GridView.builder(
itemCount: griddata.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1,
),
itemBuilder: (context, index) {
return GridSingleItem(itemGriddata:
griddata[index],
onTap:(){
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
PageX(griddata[index])));
}
);
},
),
),
),
],
));
}
}
ok I figured it out myself, turns out its literally 2 lines of code
in model, swap const to final, add url property
final griddata = [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
"url": Page1()
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
"url": Page2()
}
];
and in GridSingleItem onTap pass itemGriddata["url"]
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
const GridSingleItem({Key key, #required this.itemGriddata})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => itemGriddata["url"]));
},
Hi guys i am new to flutter. I have a grid of clickable buttons. Right now its only two, but it can grow to many. how can I refactor this to make it more dynamic and handle many future buttons? like a grid list of buttons that you can each click to navigate to different pages.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(30.0),
child: GridView.extent(
maxCrossAxisExtent: 150,
crossAxisSpacing: 15.0,
mainAxisSpacing: 15.0,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ProductScreen(),
),
);
},
padding: EdgeInsets.only(top: 33),
child: Column(
children: [
Icon(
Icons.shopping_cart_outlined,
color: Colors.white,
),
Text(
"Products",
style: TextStyle(
color: Colors.white,
),
),
],
),
),
FlatButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => MailScreen(),
),
);
},
padding: EdgeInsets.only(top: 33),
child: Column(
children: [
Icon(
Icons.mail,
color: Colors.white,
),
Text(
"Mail",
style: TextStyle(
color: Colors.white,
),
),
],
),
),
],
),
),
);
}
}
Here is a solution:
1. Define a AppAction Model
class AppAction {
final Color color;
final String label;
final Color labelColor;
final IconData iconData;
final Color iconColor;
final void Function(BuildContext) callback;
AppAction({
this.color = Colors.blueGrey,
this.label,
this.labelColor = Colors.white,
this.iconData,
this.iconColor = Colors.white,
this.callback,
});
}
You could also have the route or its name instead of a callback function. Though, a callback will allow you to define other types of actions if needed. (example: launching an external URL, triggering a modal dialog, etc.)
2. Defining your Application Actions
final List<AppAction> actions = [
AppAction(
label: 'Products',
iconData: Icons.shopping_cart_outlined,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => ProductScreen()));
},
),
AppAction(
label: 'Mails',
iconData: Icons.mail,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => MailScreen()));
},
),
AppAction(
color: Colors.white,
label: 'Urgent',
labelColor: Colors.redAccent,
iconData: Icons.dangerous,
iconColor: Colors.redAccent,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => UrgentScreen()));
},
),
AppAction(
color: Colors.green.shade200,
label: 'News',
labelColor: Colors.black,
iconData: Icons.new_releases,
iconColor: Colors.green,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => NewsScreen()));
},
),
];
3. Define a generic ActionButton
class ActionButton extends StatelessWidget {
final AppAction action;
const ActionButton({
Key key,
this.action,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return OutlinedButton.icon(
onPressed: () => action.callback?.call(context),
style: OutlinedButton.styleFrom(
backgroundColor: action.color,
padding: const EdgeInsets.all(16.0),
),
label: Text(action.label, style: TextStyle(color: action.labelColor)),
icon: Icon(action.iconData, color: action.iconColor),
);
}
}
4. Simplify your HomePage
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: 'Home Page',
child: Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(30.0),
child: GridView.extent(
maxCrossAxisExtent: 120,
crossAxisSpacing: 15.0,
mainAxisSpacing: 15.0,
children: actions.map((action) => ActionButton(action: action)).toList(),
),
),
);
}
}
Voilà! If you want, here is a full standalone code sample to play with:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class AppAction {
final Color color;
final String label;
final Color labelColor;
final IconData iconData;
final Color iconColor;
final void Function(BuildContext) callback;
AppAction({
this.color = Colors.blueGrey,
this.label,
this.labelColor = Colors.white,
this.iconData,
this.iconColor = Colors.white,
this.callback,
});
}
final List<AppAction> actions = [
AppAction(
label: 'Products',
iconData: Icons.shopping_cart_outlined,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => ProductScreen()));
},
),
AppAction(
label: 'Mails',
iconData: Icons.mail,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => MailScreen()));
},
),
AppAction(
color: Colors.white,
label: 'Urgent',
labelColor: Colors.redAccent,
iconData: Icons.dangerous,
iconColor: Colors.redAccent,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => UrgentScreen()));
},
),
AppAction(
color: Colors.green.shade200,
label: 'News',
labelColor: Colors.black,
iconData: Icons.new_releases,
iconColor: Colors.green,
callback: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => NewsScreen()));
},
),
];
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: 'Home Page',
child: Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(30.0),
child: GridView.extent(
maxCrossAxisExtent: 120,
crossAxisSpacing: 15.0,
mainAxisSpacing: 15.0,
children:
actions.map((action) => ActionButton(action: action)).toList(),
),
),
);
}
}
class AppLayout extends StatelessWidget {
final String pageTitle;
final Widget child;
const AppLayout({Key key, this.pageTitle, this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(pageTitle)),
body: child,
);
}
}
class ActionButton extends StatelessWidget {
final AppAction action;
const ActionButton({
Key key,
this.action,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return OutlinedButton.icon(
onPressed: () => action.callback?.call(context),
style: OutlinedButton.styleFrom(
backgroundColor: action.color,
padding: const EdgeInsets.all(16.0),
),
label: Text(action.label, style: TextStyle(color: action.labelColor)),
icon: Icon(action.iconData, color: action.iconColor),
);
}
}
class ProductScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: ('Products Page'),
child: Center(
child: Text('LIST OF PRODUCTS'),
),
);
}
}
class MailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: 'Mail Page',
child: Center(
child: Text('LIST OF MAIL'),
),
);
}
}
class UrgentScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: 'Urgent Page',
child: Center(
child: Text('URGENT', style: TextStyle(color: Colors.redAccent)),
),
);
}
}
class NewsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppLayout(
pageTitle: 'News Page',
child: Center(
child: Text('NEWS', style: TextStyle(color: Colors.green)),
),
);
}
}
Create a separate widget for the button and pass the color, icon, Text and route in the params.
Instead of using push in navigator use pushNamed and used the passed route name here.
I'm trying to retrieve a firestore document in my app so that I can update it. Here's the current code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Baby Names',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Baby Name Votes')),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('baby').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
final docID = record.reference.documentID;
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name),
trailing: Text(record.votes.toString()),
onTap: () {
print('Here is the record you have just clicked on: $docID, ${record.name}, ${record.votes}');
showModalBottomSheet(
context: context,
builder: (context) => EditVoteScreen(),
);
},
),
),
);
}
}
class EditVoteScreen extends StatefulWidget {
#override
_EditVoteScreenState createState() => _EditVoteScreenState();
}
class _EditVoteScreenState extends State<EditVoteScreen> {
String newBabyName = 'Gregg';
#override
Widget build(BuildContext context) {
return Container(
color: Color(0xff757575),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Edit A Baby Name'),
SizedBox(height: 20.0),
Text(
'Current Name: ${record.name}',
),
SizedBox(height: 20.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Change Baby Name To:',
),
SizedBox(
width: 20.0,
),
DropdownButton<String>(
value: newBabyName,
icon: Icon(Icons.arrow_drop_down_circle),
iconSize: 24,
elevation: 16,
underline: Container(
height: 1,
color: Color(0xFF150A42),
),
onChanged: (String newValue) {
setState(() {
newBabyName = newValue;
});
},
items: <String>['Gregg', 'Mikey', 'Joey', 'Dave']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
],
),
SizedBox(height: 20.0),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
color: Color(0xFF150A42),
textColor: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: Text(
'💾 Save Changes',
),
),
onPressed: () {},
),
],
),
),
);
}
}
class Record {
final String name;
final int votes;
final DocumentReference reference;
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['votes'] != null),
name = map['name'],
votes = map['votes'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$name:$votes>";
}
The firestore database only has two fields. Here's some sample data: name: 'James' (String), and votes: 2 (number)
When you click on a record in the app, I've managed to get it to print out the docID in the console, as well as the name and votes. The question is, how can i then take the document that I have just clicked on and display it in the ModalBottomSheet so that the name field can be updated?
If I can get it to display in the name field in the ModalBottomSheet, I should be able to figure out how to update it by myself. But I'm struggling to even get it to show up in there! My current code displays the error undefined name 'record'.
Any help would be greatly appreciated!
Thank you
Jason
You need to add a constructor to your EditVoteScreen widget and pass in the document's information so you can use it in your EditVoteScreen's build method:
class EditVoteScreen extends StatefulWidget {
final Record record;
const EditVoteScreen({Key key, this.record}) : super(key: key);
#override
_EditVoteScreenState createState() => _EditVoteScreenState();
}
Pass in the record when you create the class:
showModalBottomSheet(
context: context,
builder: (context) => EditVoteScreen(record: record,),
);
Then reference it within the state class by refering to the widget variable.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Edit A Baby Name'),
SizedBox(height: 20.0),
Text(
'Current Name: ${widget.record.name}', // Here
),
I'm testing out this Flutter project which generates a movie list from a for loop:for (int i = 0; i < 3; i++) {...}.
The result of the loop is 3 cards which I'd like to add an onTap function to and navigate to the corresponding page as a result.
Github:https://github.com/devefy/Flutter-Streaming-Service-App-UI/blob/master/lib/main.dart
After the padding on line 222, I added a ListTile() with the onTap:(){}
widget. This enabled the tap widget for the bottom half of the card.
// Line 219 to 222
Padding(
padding: EdgeInsets.only(top: 3.0),
child: Text(i == 0 ? "Season 2" : ""),
),// Padding
ListTile(onTap: (){
debugPrint('${[i]} was tapped!');
//Navigator.push(context, route)
My results when tapping the 3 cards.
flutter: [0] was tapped!
flutter: [1] was tapped!
flutter: [2] was tapped!
Where I get lost is how to actually navigate to the detail page of the movie depending on which card I tap on.
Any help is appreciated...Thank You All!!!
The best practice for something like this would be to create 2 pages, a movie list, and a details page.
The movie list will loop through all of the movies, then the on tap would point to the details page. The key here is that you can pass data to the details page when navigating. Whether that be an id or slug for the movie allowing you to fetch specific data or just an index to a list for a simpler example.
Navigator.push( context, MaterialPageRoute( builder: (context) => DetailScreen(todo: todos[index]),),);
Here is an example regarding a todo list and a details screen. I would try running this so you can understand further what I mean.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class Todo {
final String title;
final String description;
Todo(this.title, this.description);
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodosScreen extends StatelessWidget {
final List<Todo> todos;
TodosScreen({Key key, #required this.todos}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
// When a user taps the ListTile, navigate to the DetailScreen.
// Notice that you're not only creating a DetailScreen, you're
// also passing the current todo through to it.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
);
}
}
class DetailScreen extends StatelessWidget {
// Declare a field that holds the Todo.
final Todo todo;
// In the constructor, require a Todo.
DetailScreen({Key key, #required this.todo}) : super(key: key);
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
Here is the app running.
Let me know if you have any questions!
Finally found a solution I was happy with. Thanks to flutter_ui_challenge.
import 'package:flutter/material.dart';
import 'package:flutter_youtube/flutter_youtube.dart';
class YouTubeVideoList extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("YouTube Video List"),
backgroundColor: Colors.lightBlue,
elevation: 2,
actions: <Widget>[
Container(
padding: EdgeInsets.all(10),
)
],
),
body: Lists(),
);
}
}
class Item {
final String title;
final String category;
final String place;
final Function onTap;
final String image;
Item(
{this.title,
this.category,
this.place,
this.onTap,
this.image});
}
class Lists extends StatelessWidget {
final List<Item> _data = [
Item(
onTap: playYoutubeVideo1,
title: 'Gardens By the Bay',
category: "Gardens",
place: "Singapore",
image: "https://images.pexels.com/photos/672142/pexels-photo-672142.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"),
Item(
onTap: playYoutubeVideo2,
title: 'Singapore Zoo',
category: "Parks",
place: "Singapore",
image: "https://images.pexels.com/photos/1736222/pexels-photo-1736222.jpeg?cs=srgb&dl=adult-adventure-backpacker-1736222.jpg&fm=jpg"),
Item(
onTap: playYoutubeVideo3,
title: 'National Orchid Garden',
category: "Parks",
place: "Singapore",
image: "https://images.pexels.com/photos/62403/pexels-photo-62403.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"),
];
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.all(6),
itemCount: _data.length,
itemBuilder: (BuildContext context, int index) {
Item item = _data[index];
return GestureDetector(
onTap: item.onTap,
child: Card(
elevation: 3,
child: Row(
children: <Widget>[
Container(
height: 125,
width: 110,
padding:
EdgeInsets.only(left: 0, top: 10, bottom: 70, right: 20),
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(item.image),
fit: BoxFit.cover)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.title,
style: TextStyle(
color: Colors.deepOrange,
fontWeight: FontWeight.w700,
fontSize: 15),
),
Text(
item.category,
style: TextStyle(fontSize: 12, color: Colors.black87),
),
Text(
item.place,
style: TextStyle(fontSize: 10, color: Colors.black87),
),
SizedBox(
height: 10,
),
],
),
)
],
),
),
);
},
);
}
var youtube = new FlutterYoutube();
static playYoutubeVideo1() {
FlutterYoutube.playYoutubeVideoByUrl(
apiKey: "YOUR_API_KEY",
videoUrl: "YOUTUBE_VIDEO_URL",
);
}
static playYoutubeVideo2() {
FlutterYoutube.playYoutubeVideoByUrl(
apiKey: "YOUR_API_KEY",
videoUrl: "YOUTUBE_VIDEO_URL",
);
}
static playYoutubeVideo3() {
FlutterYoutube.playYoutubeVideoByUrl(
apiKey: "YOUR_API_KEY",
videoUrl: "YOUTUBE_VIDEO_URL",
);
}
}