Need help in making a reusable widget in flutter - flutter

So I am making an app with flutter. So in the main.dart file i am making a component which is basically a bunch of widgets wrapped together. I have to use this component multiple times so I thought of making these reusable component in another dart file and then importing it in main.dart.
This is my code for reusable.dart
import 'package:flutter/material.dart';
double mainTab = 150;
class TileData extends StatefulWidget {
#override
_TileDataState createState() => _TileDataState();
}
class _TileDataState extends State<TileData> {
#override
Widget build(BuildContext context) {
return Container(
height: 200 - 15.0,
width: mainTab - 10.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 0, 0),
child: Column(
),
),
);
}
}
I plan to use this TileData Widget in my main.dart in this manner
ListView(
children: children: <Widget>[
TileData(
children: <Widget>[
Text('Element 1'),
]),
TileData(
children: <Widget>[
Text('Element 2'),
]),
TileData(
children: <Widget>[
Text('Element 3'),
],
)
],
),
So the children of the TileData() widget are actually the children of the column which was last wrapped in the widget in reusable.dart
Is there any way I can achieve this?

TileDate
import 'package:flutter/material.dart';
double mainTab = 150;
class TileData extends StatefulWidget {
List<Widget> widgetsList;
TileData({this.widgetsList});
#override
_TileDataState createState() => _TileDataState();
}
class _TileDataState extends State<TileData> {
#override
Widget build(BuildContext context) {
return Container(
height: 200 - 15.0,
width: mainTab - 10.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 0, 0),
child: Column(
children: widget.widgetsList,
),
),
);
}
}
main
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/widgets/TileData.dart';
void main() => runApp(MaterialApp(
home: Scaffold(backgroundColor: Color(0xFF2d3447), body: MyApp()),
));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
TileData(
widgetsList: [Text("Element 1")],
),
TileData(
widgetsList: [Text("Element 2")],
),
TileData(
widgetsList: [Text("Element 3")],
)
],
);
}
}
In this way u can reuse

Create a property and use it as an argument in the constructor of the reusable widget.
final List<Widget> children;
TileData({this.children});
Then, in your build method, pass the property to the column.
Column(
children: widget.children
)

You can use ListView.builder()
add Constructor in your TileData widget
something like
ListView.builder(
itemCount:data.length,
itemBuilder: (context,index){
return TileData(data:"Element $index");
}
)

Related

Flutter - How to setup a custom height and width of Scaffold

I'm aware that MediaQuery solutions exist to problems, however, I want to limit the size of my Scaffold so that it can be used for web-based apps as well. Similar to what Instagram has, can anyone help me with it?
Have you tried wrapping your Scaffold in SafeArea with a minimum property of EdgeInsets.all(32.0)?
For me, this recreates your mockup on any screen
Example code:
//...
return SafeArea(
minimum: const EdgeInsets.all(32.0),
child: Scaffold(
//...
),
);
//...
I used sizedboxes to create layers for a single page look. The gridview does not shrink to the size of the window. Instead it activates scrolling. My solution works for chrome web.
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(
primarySwatch: Colors.purple,
buttonTheme: const ButtonThemeData(
textTheme:ButtonTextTheme.primary,
buttonColor:Colors.yellow,
)
),
home: Test_SinglePage(),
);
}
}
class DataRecord{
String name;
String number;
DataRecord(this.name,this.number);
}
class Test_SinglePage extends StatefulWidget {
Test_SinglePage({Key? key}) : super(key: key);
#override
State<Test_SinglePage> createState() => _Test_SinglePageState();
}
class _Test_SinglePageState extends State<Test_SinglePage> {
List<DataRecord> lstData=[
DataRecord("A","1"), DataRecord("B","2"), DataRecord("C","3"), DataRecord("D","4"),
DataRecord("E","5"), DataRecord("F","6"), DataRecord("G","7"), DataRecord("H","8"),
DataRecord("I","9"), DataRecord("J","10"), DataRecord("K","11"), DataRecord("L","12"),
DataRecord("M","13"), DataRecord("N","14"), DataRecord("O","15"), DataRecord("P","16"),
DataRecord("Q","17"), DataRecord("R","18"), DataRecord("S","19"), DataRecord("T","20"),
DataRecord("V","21"), DataRecord("X","22"), DataRecord("Y","23"), DataRecord("Z","24"),
];
Widget _dialogBuilder(BuildContext context, String name)
{
return SimpleDialog(
contentPadding:EdgeInsets.zero,
children:[
Container(width:80,height:80,child:
Column(children:[
Text(name),
SizedBox(height:20),
Expanded(child:Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ElevatedButton(onPressed:(){ Navigator.of(context).pop();}, child:
Text("Close"))
],))
])
)]);
}
Widget _itemBuilder(BuildContext context,int index)
{
return
GestureDetector(
onTap:()=>showDialog(context:context,builder:(context)=>_dialogBuilder(context,lstData[index].name)),
child:Container(color:Colors.grey,child:GridTile(child: Center(child:
Column(children:[
Text(lstData[index].name,style:Theme.of(context).textTheme.headline2),
Text(lstData[index].number,style:Theme.of(context).textTheme.headline4)
])
))));
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title:Text("Single Page")),body:
Container(
margin: const EdgeInsets.only(top:20.0, left: 20.0, right: 20.0, bottom:10.0),
child:
Flex(
direction: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child:Row(
mainAxisAlignment: MainAxisAlignment.start,
children:[
FittedBox(
fit:BoxFit.fitHeight,
child:SizedBox(
width:200,
height:200,
child: Image.asset("assets/images/apple.jpg"),
)),
Column(
mainAxisAlignment:MainAxisAlignment.start,
children:[
Row(
children: [
SizedBox(height:100,width:200,child:Container(color:Colors.red,child:Text("reached"))),
SizedBox(height:100,width:200,child:Container(color:Colors.blue,child:Text("reached2"))),
SizedBox(height:100,width:200,child:Container(color:Colors.green,child:Text("reached3")))
],),
Row(children: [
SizedBox(width:600, child:ElevatedButton(
onPressed:(){
},child:Text("Press Me")))],)
])
])),
Expanded(child:SizedBox(
height:400,
width:MediaQuery.of(context).size.width,child:
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: lstData.length,
itemBuilder: _itemBuilder
)))],)
,));
}
}

Flutter Web: MaterialApp Title changes every time I pop back

Im building a personal website and everytime I pop back from Projects or Blog Page to my home page the Material App changes from the title i initially put it to the name of the folder carpet of the project. I still don't understand why this happens, so any help would be greatly appreciated.
Note: I'm using the Fluro package for my navigation route.
Image representation of how the MaterialApp Title changes
Blog Page =>Home Page
blog_page.dart
import 'package:flutter/material.dart';
class BlogPage extends StatefulWidget {
#override
_BlogPageState createState() => _BlogPageState();
}
class _BlogPageState extends State<BlogPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: WillPopScope(
onWillPop: () async => true,
child: Center(
child: FractionallySizedBox(
widthFactor: 0.8,
child: FittedBox(
fit: BoxFit.fill,
child: Center(
child: Text(
'Hello Stranger!',
style: Theme.of(context).textTheme.headline1,
),
),
),
),
),
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:webapp/router.dart';
void main() {
FluroRouter.setupRouter();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Personal Website',
initialRoute: 'home',
onGenerateRoute: FluroRouter.router.generator,
);
}
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'package:webapp/widgets/social_media.dart';
import 'package:webapp/widgets/wave_body.dart';
import 'package:webapp/widgets/custom_button_border.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
HomePage() {
timeDilation = 1.0;
}
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
Size size = new Size(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height,
);
return DesktopLayout();
}
}
class DesktopLayout extends StatelessWidget {
const DesktopLayout({
Key key,
#required this.size,
}) : super(key: key);
final Size size;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(47, 66, 83, 1.0),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
flex: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
flex: 1,
child: Container(),
),
Flexible(
flex: 1,
child: ProfessionalSocialMedia(),
),
Flexible(
flex: 1,
child: Container(),
),
Flexible(
flex: 1,
child: PersonalSocialMedia(),
),
Flexible(
flex: 1,
child: Container(),
),
],
),
),
SizedBox(
height: 15.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomButtonBorder(
stringText: 'Projects',
size: size,
onPressed: () {
Navigator.pushNamed(context, 'project');
},
),
SizedBox(
width: 50.0,
),
CustomButtonBorder(
stringText: 'Blog',
size: size,
onPressed: () {
Navigator.pushNamed(context, 'blog');
},
)
],
),
Stack(
children: [
WaveBody(
size: size,
xOffset: 0,
yOffset: 0,
color: Color.fromRGBO(21, 160, 132, 1.0),
),
],
),
],
),
);
}
}
The Fluro package could be easily be using the Title Widget which changes the Tab name.
That Widget takes a "title (String)" and a "color (Color)" and will update the name over the Tab.
If you're only using Flutter Web you can take advantage of the http class and also replace your url to match your title:
#override
void initState() {
super.initState();
window.history.pushState(null, 'Blog Page', 'blog-page');
}
That will update your URL to "https://my-url.com/blog-page" and adding the Title Widget your tab will say "Blog Page" as well.
#override
Widget build(BuildContext context) {
return Material(
child: Title(
title: 'Blog Page',
color: Colors.white,
child: Container(),
),
);
}
If by any reason you also need Mobile, change your: import 'dart:html'; for the library "universal_html": https://pub.dev/packages/universal_html

flutter, Why not render ListView<Widget> when build method called?

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
}
}
class TestWidget extends StatefulWidget {
TestWidgetState createState() => new TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
List<Widget> _bodyItems = [];
List<Widget> _topItems = [];
final Widget _boundary = Column(
children: <Widget>[
SizedBox(
height: 10,
),
SizedBox(
child: Container(
color: Colors.black12,
),
height: 1,
),
SizedBox(
height: 10,
),
],
);
#override
void initState() {
super.initState();
Widget e = GestureDetector(key: Key("0"), onTap: () {
onChangedFunction(Key("0"));
},child: Text("This text is on body range"));
_bodyItems.add(e);
_topItems = [];
}
void onChangedFunction(Key key) async {
setState(() {
_bodyItems.removeAt(0);
_topItems.add(Text("This text is on top range."));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
SizedBox(
height: 10,
),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 100,
),
child: ListView(
scrollDirection: Axis.horizontal,
children: _topItems,
),
),
_boundary,
ConstrainedBox(
constraints: BoxConstraints(
minHeight: 450,
maxHeight: 450,
),
child: Column(
children: _bodyItems,
),
),
_boundary,
],
)));
}
}
result
top items : [Text("This text is on top range.")]
This code deletes the body item when the widget of the body item is tapped and added item at the top item.
Looking at the result, you can see that the data disappeared from the body item widget and the data was added to the top item widget.
However, the data of the top item is not render.
I want to know what the reason is.
This happens because Children is constant, you can see that if you look into definition.
You will find following line in implementation of ListView.
List<Widget> children = const <Widget>[],
but you can change widget in side children, so you have to give list as a children of list.
As Foolowing.
children: [..._topItems],
Replace _topItems and _bodyItems with below
children: [..._topItems], & children: [..._bodyItems], --inside your body

How to add custom widget when button is pressed

I used a couple other threads to create an app where you type in some text in a textfield and when you press the button a default container is added to a list with the text in one of the fields. However when I type the text and add the widget the text is changed for all entries instead of just for the one that was added. This is my code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
#override
Widget build(BuildContext context) {
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
InputWidget(this.index, {Key key, this.noteRec}) : super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
);
}
}
How can I make the Text different with every entry?
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
In this code, you set the input text for all the elements in children. It's the reason all the entries are changed to the same text. You can save the text to a list of the string when you press the save button and call it in List.generate:
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: listString[i],
));
Try this code
Container(
height: 200,
child: Column(
children: <Widget>[
Expanded(
child: buildGridView(),
)
],
),
),
Then call the buildGridView that will return Widgets
Widget buildGridView() {
return Text('text here'); // your future widget
}

How to arrange boxes in row in Flutter?

This is my code to arrange boxes vertically.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
Widget box({width: 100.0, height: 100.0}) => Container(
width: width,
height: height,
color: Colors.grey,
);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}
Since I have a little experience for CSS flexbox, replacing the Column widget with the Row to arrange boxes in row But it can't work. Only black screen appeared. I've already make sure that the code works under the MaterialApp widget.
For learning purpose, I want to keep the code as simple as possible.
What do I need to make the above code work? What is a minimum requirement? Or is there an other widget appropriates for root in such example case?
Thank you for sparing time to read my question!
You need a material app render the colors and stuff
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
);
}
}
MaterialApp widget must be a root widget
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp())); // <- magic
class MyApp extends StatelessWidget {
Widget box({width: 100.0, height: 100.0}) {
return Container(
width: width,
height: height,
color: Colors.grey,
);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}