I am very new to flutter and I am trying to create a Generic Button widget that I can just pass parameters into (Text, color, etc.) keeping it short to just text right now. So I have setup my main app named SplashScreen and in the body I add the GenericButton class. I would like to know if there is a way for me to pass a string of text or any other kind of data, save that in my GenericButton class so that I can push that into my _GenericButtonState using widget.buttonText
final String _title = "Flutter Demo";
// * This is the landing page
class SplashScreen extends StatelessWidget {
// * This widget is the root of your application.
const SplashScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: Scaffold
(
appBar: AppBar
(
title: Text(_title)
),
body: GenericButton() // <-- Statelful Widget I would like to pass data into.
)
);
}
}
// * Creating reusable button
class GenericButton extends StatefulWidget
{
final String buttonText;
const GenericButton(this.buttonText);
#override
_GenericButtonState createState() => _GenericButtonState();
}
class _GenericButtonState extends State<GenericButton>
{
#override
Widget build(BuildContext context)
{
return OutlinedButton(
child: Text(widget.buttonText),
onPressed: ()
{
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LocationsPage()),
);
},
);
}
}
you can do that normally through constructor , this is working example from one of my projects
class PrimaryButton extends StatelessWidget {
final String text;
final Color color;
final Color textColor;
final Function onTap;
final EdgeInsets edgeInsets;
final bool pending;
const PrimaryButton({
Key key,
#required this.text,
#required this.onTap,
this.color = AppTheme.primaryColor,
this.textColor = AppTheme.secondaryColor,
this.edgeInsets = const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
this.pending = false,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: pending ? null: onTap,
child: Container(
child: Center(
child: pending
? Padding(
padding: const EdgeInsets.all(5.0),
child: SpinKitThreeBounce(
color: AppTheme.scaffoldBackgroundColor,
size: 15.0,
),
)
: Text(
text,
style: AppTheme.textTheme.headline5.copyWith(fontSize: 18.0, fontWeight: FontWeight.bold,color: textColor),
),
),
margin: edgeInsets,
padding: EdgeInsets.all(12.0),
width: double.infinity,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(8.0)),
),
);
}
}
You have already defined the buttonText parameter. You have to pass a text into it (like the _title), and anytime you want a new text pass that as the new parameter.
Your GenericButton is a StatefulWidget, if the buttonText parameter changes, the widget won't redraw itself. The didChangeDependencies method will be fired and you need to handle the changes manually: update any state inside the _GenericButtonState
But:
If you change the GenericButton to StatelessWidget, any time the buttonText parameter changes the widget will redraw itself.
Conclusion:
As I understood what you want to build, you'd better have a GenericButton StatelessWidget with the parameters and pass them from the parent, which could be the StatefulWidget as that will manage the state of the texts and other arguments.
You can read more about state management here: https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
Related
when pressed on quick search the container should expand like this and I don't want to use expandable or any other widget because I want to use the animation of animated container when opened and closed
All credit goes to this post answer: How to create an animated container that hide/show widget in flutter
Here is a Complete Working Code:
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app,
try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the
application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Animated Container Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful,
meaning
// that it has a State object (defined below) that contains fields that
affect
// how it looks.
// This class is the configuration for the state. It holds the values
(in
this
// case the title) provided by the parent (in this case the App widget)
and
// used by the build method of the State. Fields in a Widget subclass
are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _height = 50.0;
bool _isExpanded = false;
Future<bool> _showList() async {
await Future.delayed(Duration(milliseconds: 300));
return true;
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as
done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build
methods
// fast, so that you can just rebuild anything that needs updating
rather
// than having to individually change instances of widgets.
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(widget.title),
),
body: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: _height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.grey,
),
width: MediaQuery.of(context).size.width - 100,
padding: EdgeInsets.only(left: 15, right: 15),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Title'),
InkWell(
onTap: () {
if (!_isExpanded) {
setState(() {
_height = 300;
_isExpanded = true;
});
} else {
setState(() {
_height = 50;
_isExpanded = false;
});
}
},
child: Container(
height: 30,
width: 40,
color: Colors.red,
child:
!_isExpanded ? Icon(Icons.add) :
Icon(Icons.remove),
),
),
],
),
),
_isExpanded
? FutureBuilder(
future: _showList(),
/// will wait untill box animation completed
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SizedBox();
}
return ListView.builder(
itemCount: 10,
shrinkWrap: true,
itemBuilder: (context, index) {
return Text('data'); // your custom UI
},
);
})
: SizedBox.shrink(),
],
),
));
}
}
use expandable: flutter pub add expandable,
check the documentation here.
Excuse me guys, i tryin to build a new page, but with variable "nextcode" in it. so in the new page, it will show nextcode text
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Lemari(nextcode)));
},
but in the line "Widget build(String Kode) {" it must be like this "Widget build(BuildContext context) {"
class Lemari extends StatelessWidget {
#override
Widget build(String Kode) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: Text('this is th next page'),
),
backgroundColor: Colors.white,
body: Center(
child: Column(
children: [
Container(height: 100, width: 100, color: Colors.red, child: Text('hhe'),),
]
),
),
);
}
}
So anyone who can help me ? please :(
You don't have to change the build method parameters instead you should add a new parameter in the widget and require it
Example
const MyPageView({Key? key}) : super(key: key);
Here you can add another parameter.
Then inside the class you define that parameter.. so when you make a new MyPageView you will have to pass the newly added parameter
Bye :)
Your code should have a compiler error
In Lemari , you never declare nextcode and constructor also do not have parameter nextcode.
You can try like this, add
class Lemari extends StatelessWidget {
final String nextcode;
const Lemari({Key key, this.nextcode}) : super(key: key);
#override
Widget build(BuildContext context) {}
}
if your nextcode is String
Hello Guys im new to flutter.
To understand Flutter I watched a lot of videos and read blog entries.
But there is always a problem:
Each video is about a specific topic and all of them start with a new Flutter project. As long as I want to continue working on the code I can't change the code.
Below I have added a code by Hanz Müller as an example. Topic NavigationBar.
But now I want to delete the text under the icons and edit the different app pages (body) with text and images.
I can't delete the text under the icons because text can't be ''null''.
And I can't edit the diffrent body pages because I can't find the position.
i only know html and css because it is a hobby and now i search for the place where i find the body container :)
Thanks a lot for your help
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class Destination {
const Destination(this.title, this.icon, this.color);
final String title;
final IconData icon;
final MaterialColor color;
}
const List<Destination> allDestinations = <Destination>[
Destination('Home', Icons.home, Colors.teal),
Destination('Business', Icons.business, Colors.cyan),
Destination('School', Icons.school, Colors.orange),
Destination('Flight', Icons.flight, Colors.blue)
];
class DestinationView extends StatefulWidget {
const DestinationView({ Key key, this.destination }) : super(key: key);
final Destination destination;
#override
_DestinationViewState createState() => _DestinationViewState();
}
class _DestinationViewState extends State<DestinationView> {
TextEditingController _textController;
#override
void initState() {
super.initState();
_textController = TextEditingController(
text: 'sample text: ${widget.destination.title}',
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widget.destination.title} Text'),
backgroundColor: widget.destination.color,
),
backgroundColor: widget.destination.color[100],
body: Container(
padding: const EdgeInsets.all(32.0),
alignment: Alignment.center,
child: TextField(controller: _textController),
),
);
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin<HomePage> {
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
index: _currentIndex,
children: allDestinations.map<Widget>((Destination destination) {
return DestinationView(destination: destination);
}).toList(),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
items: allDestinations.map((Destination destination) {
return BottomNavigationBarItem(
icon: Icon(destination.icon),
backgroundColor: destination.color,
title: Text(destination.title)
);
}).toList(),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage(), debugShowCheckedModeBanner: false));
}
If you want to remove the Text under the icon Check the code where the Text widget is place.
So you have the relevant Text widget in BottomNavigationBarItem
title: Text(destination.title)
So if you don't need the Text widget you can simply replace it with Container to display nothing.
title: Text(destination.title)
I would suggest you read the code and understand it will. The better you understand how your widgets are built and rendered it will be easier to modify them.
The class in question is invoked from another page with the line
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) =>
ProPage(iD: bestRatedPros[index]["ID"])));
},
Where bestRatedPros is a list of maps with the variable iD for the following class -
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ProPage extends StatefulWidget {
ProPage({Key key, this.iD}) : super(key: key);
final iD;
#override
_ProPageState createState() => _ProPageState(iD);
}
class _ProPageState extends State<ProPage> {
int iD;
_ProPageState(this.iD);
#override
void initState() {
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
extendBodyBehindAppBar: true,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.white, //change your color here
),
elevation: 0,
backgroundColor: Colors.amber
),
body:
Text("EWFWEFEWEWFWEF",style: TextStyle(color: Colors.black))
);
}
}
The getDataFromBackend function and
all the variables associated with it was meant to be within the body. But Nothing shows up in the body no matter what it is. Even a simple Text widget doesn't. I'm only trying to pass the variable iD from one page to the other without complicating things. The Run log doesn't show any Errors or warnings.
Arun,
See below where your Text is:
Reason for that is that you specified:
extendBodyBehindAppBar: true,
on your Scaffold, so body is expanded and top part of it is hidden behind AppBar
I am flutter beginner.
I have my main page split into 2 custom widgets, top widget and page widget. And I have a drawer with a few buttons to change the content of the page widget.
I followed this tutorial and try to swap the widget as per click on the buttons.
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) {
var currentpage = 'landing';
Widget pageWidget;
switch (currentpage) {
case 'landing':
pageWidget = new Landing();
break;
case 'visitors':
pageWidget = new Visitors();
break;
default:
}
return Scaffold(
body:new Container(
child: Container(
child: Column(
children: <Widget>[
Header(), ///<-- widget declared in separated .dart file
pageWidget
],
)
)
drawer: Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Text(
"UserN3",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
),
new GestureDetector(
onTap: (){
print("landing");
Future.delayed(Duration.zero,(){
setState(() {
currentpage = 'landing';
});
});
},
child: new Text("landing"),
),
new GestureDetector(
onTap: (){
print("visitors");
Future.delayed(Duration.zero,(){
setState(() {
currentpage = 'visitors';
});
});
},
child: new Text("Visitors"),
),
],
),
),
);
}
so the gist is:
I have switch case in the build, which will change the pageWidget accordingly based on currentpage, which will be changed by buttons in the drawer.
But keep getting the same error:
Another exception was thrown: setState() or markNeedsBuild() called when widget tree was locked.
As you can see I added Future.delayed() based on this suggestion but the error persist.
After some researching, I got a feeling that it related to how drawer deal with setState(), but I am not sure how.
Obviously I still not getting how flutter work in terms of build and state.
What seems to be the problem and how can I resolve this?
You should declare your currentPage variable outside your build method, because every time you call setState the build method is called again and the variable doesn't change.
So move this :
Widget build(BuildContext context) {
var currentpage = 'landing';
To this:
var currentpage = 'landing';
Widget build(BuildContext context) {