Flutter - how to do i forward mapped data to another page - flutter

I have three files - companies.dart, Company_Card_Style.dart and Company_Details.
I'm mapping company parameters to company_card_Style from companies.dart, now i need to send the same data to companyDetails.dart, what should i pass in MaterialPageroute and how to receive in companyDetails
Companies file - in itembuilder, im passing a company detail to companycardstyle
import 'package:flutter/material.dart';
import './Object.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'UI_Styles/Company_Card_Style.dart';
import 'dart:convert';
class Companies extends StatefulWidget {
#override
_CompaniesState createState() => _CompaniesState();
}
class _CompaniesState extends State<Companies> {
Future<List<Company>> _getCompanies() async {
var data = await http.get("http://localhost/crm_demo/getData.php");
var jsonData = json.decode(data.body);
List<Company> companies = [];
for (var c in jsonData) {
Company company = Company(
c["name"],
c["address"],
c["opportunities"],
c["pipelineRevenue"],
c["revenueAchieved"],
c["city"],
c["state"],
c["country"],
c["zipcode"],
c["phone"],
c["timezone"],
c["tags"]);
companies.add(company);
}
return companies;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Companies",
style: TextStyle(fontWeight: FontWeight.w600),
)),
body: FutureBuilder(
future: _getCompanies(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return CompanyCardStyle(company: snapshot.data[index]);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}));
}
}
CompanyCardStyle.dart - Here on click of company name i want to redirect to detail page.
import 'package:flutter/material.dart';
import '../Object.dart';
import '../Detail_Pages/Company_details.dart';
class CompanyCardStyle extends StatelessWidget {
final Company company;
CompanyCardStyle({this.company});
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 0.0),
child: ExpansionTile(
tilePadding: EdgeInsets.only(left: 10.0, right: 10.0),
leading: CircleAvatar(radius: 20.0, child: Text(company.name[0])),
subtitle: Flexible(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CompanyDetails(company: company)),
);
},
child: Text(
company.address,
overflow: TextOverflow.ellipsis,
),
)),
title:
Text(company.name, style: TextStyle(fontWeight: FontWeight.w600)),
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
companyLabels('Phone Number'),
companyLabels('Opportunities'),
companyLabels('Pipeline Revenue'),
companyLabels('Revenue Achieved'),
],
),
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
companyValues(company.phone.toString()),
companyValues(company.opportunities),
companyValues(company.pipelineRevenue.toString()),
companyValues(company.revenueAchieved.toString())
],
),
)
]))
],
));
}
Widget companyLabels(String values) {...}
Widget companyValues(String values) {...}
}
CompanyDetails.dart - Let me know how should i get data here.
import 'package:flutter/material.dart';
class CompanyDetails extends StatefulWidget {
#override
_CompanyDetailsState createState() => _CompanyDetailsState();
}
class _CompanyDetailsState extends State<CompanyDetails> {
#override
Widget build(BuildContext context) {
return Container(
child: Text("I have to display rest of the company data here"),
);
}
}

You could try this on CompanyDetails.dart
class CompanyDetails extends StatefulWidget {
CompanyDetails ({Key key, this.company})
: super(key: key);
final Company company;
#override
_CompanyDetailsState createState() => _CompanyDetailsState();
}
class _CompanyDetailsState extends State<CompanyDetails> {
Company company;
#override
void initState() {
company= widget.company;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: Text("My company name is ${company.name}"),
);
}
}

Related

How to implement streamBuilder in multiples pages to have an updated data in all pages using a single stream?

I made a program to show what problem I'm facing with streamBuilders distributed in several pages connected in one single stream. Git here: https://github.com/EuHigorBarbosa/stream_builder_test.git
The mini program was build with 3 pages: A -> B -> C.
The problem is when I upgrade the data of stream in page C, the streamBuilder in page C upgrades the data with no problem. But when I pop the page to the page B, the streamBuilder of page B didn't update the data of snapshot and show up the old value. Both streamBuilders are connected with the same stream.
But when I go back to the page A and go forward to page B, the value of B is now update.
My goal is to show the update data to the user, always, in all pages. But I don't understand what I'm doing wrong.
My stream is a MultiStreamController inside a singleton class.
Page A
class PageAA extends StatefulWidget {
const PageAA({Key? key}) : super(key: key);
#override
State<PageAA> createState() => _PageAAState();
}
class _PageAAState extends State<PageAA> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return Container(
//decoration: BackGroundImage.BackGroundImageBoxDecoration,
child: Scaffold(
appBar: AppBar(title: Text('Page A')),
//backgroundColor: Colors.transparent,
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(child: Text('Start the program in page C', style: TextStyle(color: Colors.black, fontSize: 20))),
IconButton(icon: Icon(Icons.arrow_forward_ios), onPressed: ()=>Navigator.of(context).pushNamed('/pageB'),)
],
),
),
),
);
});
}
}
Page B
class PageBB extends StatefulWidget {
const PageBB({Key? key}) : super(key: key);
#override
State<PageBB> createState() => _PageBBState();
}
class _PageBBState extends State<PageBB> {
SeqDepService seqDepService = SeqDepService();
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return Container(
//decoration: BackGroundImage.BackGroundImageBoxDecoration,
child: Scaffold(
appBar: AppBar(title: Text('Page B')),
//backgroundColor: Colors.transparent,
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(child: StreamBuilder<List<SeqDepEntity>>(
stream: seqDepService.activeSeqDepListStream,
builder: ((context, snapshot) {
if(snapshot.hasData){
return (Text('The lenght of last \nSnapshot data is: ${snapshot.data!.length}', style: TextStyle(color: Colors.black, fontSize: 20)));
} else {
return Container();
}
})),),
SizedBox(height: 50,),
Center(child: Text('Enter items on Stream in page C', style: TextStyle(color: Colors.black, fontSize: 15))),
IconButton(icon: Icon(Icons.arrow_forward_ios), onPressed: ()=>Navigator.of(context).pushNamed('/pageC'),)
],
),
),
),
);
});
}
}
Page C
class PageCC extends StatefulWidget {
const PageCC({Key? key}) : super(key: key);
#override
State<PageCC> createState() => _PageCCState();
}
class _PageCCState extends State<PageCC> {
SeqDepService seqDepService = SeqDepService();
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return Container(
//decoration: BackGroundImage.BackGroundImageBoxDecoration,
child: Scaffold(
appBar: AppBar(title: Text('Page C')),
//backgroundColor: Colors.transparent,
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<List<SeqDepEntity>>(
stream: seqDepService.activeSeqDepListStream,
builder: ((context, snapshot) {
if(snapshot.hasData){
return (Text('The lenght of last \nSnapshot data is: ${snapshot.data!.length}', style: TextStyle(color: Colors.black, fontSize: 20)));
} else {
return Container();
}
})),
Center(child: IconButton(icon: Icon(Icons.add,size: 30, color: Colors.blue,), onPressed: ()=>seqDepService.addActiveSeqDep(SeqDepEntity(id:'unique', content:1)),))
],
),
),
),
);
});
}
}
Stream Class (Abstract and implemented)
abstract class SeqDepService {
List<SeqDepEntity> get activeSeqDepList;
Stream<List<SeqDepEntity>> get activeSeqDepListStream;
/// Add a single item on stream data. The stream data is a List<SeqDepEntity>
addActiveSeqDep( SeqDepEntity entity);
factory SeqDepService() {
return SeqDepServiceFFmpeg();
}
}
(Implemented)
class SeqDepServiceFFmpeg implements SeqDepService {
static SeqDepServiceFFmpeg? _instance;
SeqDepServiceFFmpeg._();
factory SeqDepServiceFFmpeg(){
_instance ??= SeqDepServiceFFmpeg._();
return _instance!;
}
static List<SeqDepEntity> _activeListSeqDep = [];
static MultiStreamController<List<SeqDepEntity>>? _controller;
#override
static final _activeSeqDepStream = Stream<List<SeqDepEntity>>.multi( (controller) {
_controller = controller;
_addInStream(_activeListSeqDep);
}, isBroadcast: true);
#override
List<SeqDepEntity> get activeSeqDepList => _activeListSeqDep;
#override
Stream<List<SeqDepEntity>> get activeSeqDepListStream => _activeSeqDepStream;
#override
void addActiveSeqDep( SeqDepEntity seqDep)async {
_activeListSeqDep.add(seqDep);
_addInStream(_activeListSeqDep);
}
static void _addInStream (List<SeqDepEntity> activeEntities){
dev.log('The number of items on Stream is: ${activeEntities.length}');
_controller?.add(activeEntities);
}
}
The entity SeqDepEntity
class SeqDepEntity {
String id;
int content;
SeqDepEntity({
required this.id,
required this.content,
});
}
I tried to implement the get_it package but it works with the same way...nothing changes.
Please, help me...

How to create load more listview in flutter

I want to create load more scrollview in listview. My app flow is storing youtube link in csv file and fetch this link from my app and display in my listview. But the problem is I don't want to wait too much load time when app is open.If I have a lot of youtube link in my csv.I will take a lot of time.So,for example I want to display only 5 video in initial state and when load more, display more 5 video in my list view.How can I do that.My code is below.
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'videolist.dart';
import './models/models.dart';
import 'package:csv/csv.dart' as csv;
import 'package:http/http.dart' as http;
class DisplayVideo extends StatefulWidget {
String id;
#override
DisplayVideo(this.id);
_DisplayVideoState createState() => _DisplayVideoState();
}
class _DisplayVideoState extends State<DisplayVideo> {
late YoutubePlayerController _controller ;
Future<List<YoutubeDetail>> _loadCSV() async {
Map<String, String> allData = {
'login': '',
'password': '',
};
final Uri url = Uri.parse(
'https://raw.githubusercontent.com/JornaldRem/bedtime_story/main/videoId.csv');
final response = await http.get(url);
csv.CsvToListConverter converter =
new csv.CsvToListConverter(eol: '\r\n', fieldDelimiter: ',');
List<List> listCreated = converter.convert(response.body);
// the csv file is converted to a 2-Dimensional list
List<YoutubeDetail> youtubeDetailList = [];
for (int i = 0; i < listCreated.length; i++) {
YoutubeDetail temp = YoutubeDetail(
listCreated[i][0],
listCreated[i][1],
);
youtubeDetailList.add(temp);
}
return youtubeDetailList;
}
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = YoutubePlayerController(
initialVideoId: widget.id,
flags: YoutubePlayerFlags(
autoPlay: true,
mute: false,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text('Title'),
toolbarHeight: 60,
backgroundColor: const Color(0xFF006666),
),
body: Column(
children: [
Container(
child: YoutubePlayer(
controller: _controller,
liveUIColor: Colors.amber,
),
),
Expanded(
child: Container(
child: FutureBuilder(
future: _loadCSV(),
builder: (BuildContext context,
AsyncSnapshot<List<YoutubeDetail>> snapshot) {
if (snapshot.hasData) {
List<YoutubeDetail> videoDetail = snapshot.data!;
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: videoDetail.length,
itemBuilder: (_, int index) {
if (index > 0) {
return GestureDetector(
child: Container(
height: 80,
child: DisplayVideoView(
videoDetail[index].url,
videoDetail[index].title),
),
onTap: (){
String url = videoDetail[index].url;
String id = url.substring(url.length - 11);
print("HEllo");
_controller.load(id);
// DisplayVideo(id);
}
);
} else {
return Container();
}
});
} else {
return Container();
}
}),
),
),
],
));
}
}
class DisplayVideoView extends StatelessWidget {
String videopath;
String title;
DisplayVideoView(this.videopath, this.title);
#override
Widget build(BuildContext context) {
String url = videopath;
String id = url.substring(url.length - 11);
// TODO: implement build
return Card(
clipBehavior: Clip.antiAlias,
child: Container(
height: 150,
padding: const EdgeInsets.all(0),
child: Row(children: [
Expanded(
flex: 6,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://img.youtube.com/vi/$id/mqdefault.jpg'),
fit: BoxFit.fill)),
),
),
Spacer(
flex: 1,
),
Expanded(
flex: 14,
child: Container(
padding: const EdgeInsets.only(top: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold)),
],
),
),
),
]),
),
);
}
}
What do you think about this approach:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<Widget> _myList = [];
void _loadFiveMore() {
_myList = <Widget>[
..._myList,
for (int i = _myList.length; i < _myList.length + 5; i++)
ListTile(title: Text('item $i')),
];
}
#override
void initState() {
_loadFiveMore();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(children: [
..._myList,
OutlinedButton(
onPressed: () {
setState(() => _loadFiveMore());
},
child: const Text('get 5 more'))
]),
),
);
}
}
void main() {
runApp(const ExampleWidget());
}
You can use this package.
have loadmore callback, refresh call back
https://pub.dev/packages/loadmore_listview

Getting error: Box<dynamic>' can't be assigned to the parameter type 'ValueListenable<dynamic>'

I'm new to flutter and I'm using Hive as my DB. I'm trying to use the ValueListenableBuilder Widget to listen to new DB updates. But I'm getting the below error.
The argument type 'Box<dynamic>' can't be assigned to the parameter type 'ValueListenable<dynamic>'.
However I have used the same code on list_session Widget but I'm not facing any errors with that. However with the second Widget I'm having this error. I'm pasting the codes for reference.
Please find my code below.
main.dart file
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:time_tracker/models/sessions.dart';
import 'pages/home.dart';
import 'pages/create_session_form.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Inititalise Hive DB
final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDirectory.path);
Hive.registerAdapter(SessionAdapter());
await Hive.openBox('tasks');
await Hive.openBox('tags');
Color themeColor = Colors.purple;
runApp(MaterialApp(
// home: Home(),
initialRoute: '/',
routes: {
'/': (context) => FutureBuilder(
future: Hive.openBox('sessions'),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.connectionState == ConnectionState.done) {
return Home();
} else {
return Scaffold();
}
}
),
'/create_session': (context) => CreateSessionForm(themeColor: themeColor)
},
));
}
create_session_form.dart file
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:time_tracker/models/sessions.dart';
import 'package:time_tracker/models/tags.dart';
import 'package:time_tracker/models/tasks.dart';
import 'package:time_tracker/my_widgets/dropdown_widget.dart';
class CreateSessionForm extends StatefulWidget {
final Color themeColor;
final DateTime activeDate;
final Function changeDate;
CreateSessionForm ({Key key, this.themeColor, this.activeDate, this.changeDate}) : super(key: key);
#override
CreateSessionFormState createState() => CreateSessionFormState(themeColor: this.themeColor, activeDate: this.activeDate, changeDate: this.changeDate);
}
class CreateSessionFormState extends State<CreateSessionForm> {
final _formKey = GlobalKey<FormState>();
Color themeColor;
DateTime activeDate;
final Function changeDate;
CreateSessionFormState({this.themeColor, this.activeDate, this.changeDate});
String taskName;
String tagName;
List<String> taskNames = [];
// List<String> tagNames = [];
String sessionDescription;
Map<String, Color> sessionTags = {};
DateTime startTime;
DateTime endTime;
bool isTaskAdded = false;
bool isTagAdded = false;
Color color;
TimeOfDay _startTime;
TimeOfDay _endTime;
Map<String, Color> colors = {
'Amber': Colors.amber,
'Blue': Colors.blue,
'Black': Colors.black,
'Orange': Colors.orange,
'Pink': Colors.pink,
'Red': Colors.red,
'Yellow': Colors.yellow
};
void addSession(Session session) {
Hive.box('sessions').add(session);
}
#override
Widget build(BuildContext context) {
// Hive.openBox('tasks');
// dynamic tasksBox = Hive.box('tasks');
return Scaffold(
appBar: AppBar(
title: Text('Create Session'),
backgroundColor: this.themeColor,
),
body: Padding(
padding: EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
ValueListenableBuilder(
valueListenable: Hive.box('tasks'),
builder: (context, tasksBox, widget) {
return MyDropDownWidget(taskName, taskNames, changeTask);
},
),
],
),
),
)
);
}
}
list_sessions - This widget works perfectly without issues
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:time_tracker/models/sessions.dart';
class ListSessions extends StatelessWidget {
ListSessions(this.activeDate);
final activeDate;
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: Hive.box('sessions').listenable(),
builder: (context, sessionsBox, widget) {
DateTime greaterDate = activeDate.add(const Duration(days: 1));
return ListView.builder(
itemCount: sessionsBox.length,
itemBuilder: (context, index) {
final sessions = sessionsBox.getAt(index) as Session;
return Card(
child: Padding(
padding: EdgeInsets.fromLTRB(10.0, 5, 10.0, 5),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
// 'Create Flutter App',
sessions.taskName,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w600
),
),
]
),
SizedBox(height: 20.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: sessions.sessionTags.entries.map((entry) {
// var w = Text(entry.value);
// doSomething(entry.key);
return Row(
children: [
Icon(Icons.circle, color: entry.value, size: 18.0),
SizedBox(width: 5.0),
Text(entry.key)
]
);
}).toList()),
Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
// Hive.box('sessions').deleteAt(index);
Hive.box('sessions').deleteAt(index);
}
),
SizedBox(height: 5.0),
Text('Completed')
],
)],
),
]
)
),
);
}
);
});
}
}
just add .listenable() after Hive.box('tasks') like you did on your ListSession widget

How to populate a form from a listview on tap in flutter

I have a form widget, a list widget, and a "wrapper" widget or in other words, a parent/container widget. So to give an idea of the widget tree, it is as such.
Parent/Container Widget
Form Widget
Button Widget
List Widget
Notice that the form, buttons and list widget are all siblings, inside of the parent/container widget. What I want to happen, is tap on a list item in the list widget, and populate the form widget with the data that gets passed from the list widget.
Here is my parent widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:andplus_flutter_7_gui/services/user_service.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'crud_form.dart';
import 'crud_list.dart';
class Crud extends StatefulWidget {
Crud({Key key, this.title}) : super(key: key);
final String title;
_CrudContainerState createState() => _CrudContainerState();
}
class _CrudContainerState extends State<Crud> {
List<User> users;
User user = User();
UserService userService;
#override
void initState() {
super.initState();
if (userService == null) {
userService = UserService(user);
}
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
userService.dispose();
}
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(
builder: (context) => Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 2,
child: StreamBuilder(
builder: (context, AsyncSnapshot<User> snapshot) {
return CrudForm(
user: snapshot.data,
onUserAdded: (user) {
userService.addUser(user);
},
);
},
stream: userService.userObservable,
),
),
Expanded(
child: Text("Future button widget"),
),
Expanded(
flex: 3,
child: StreamBuilder(
builder: (ctx, AsyncSnapshot<List<User>> snap) {
return CrudList(
onUserSelected: userService.userSelected,
users: snap.data,
);
},
stream: userService.usersObservable,
),
),
],
),
),
),
);
}
void onEditUser(User user) {
setState(() {
user = user;
});
}
}
The above widget wraps the three widgets I mentioned.
Here are the children widget:
Form:
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudForm extends StatefulWidget {
CrudForm({Key key, this.onUserAdded, this.user}) : super(key: key);
final User user;
final void Function(User user) onUserAdded;
_CrudFormState createState() => _CrudFormState(user: user);
}
class _CrudFormState extends State<CrudForm> {
_CrudFormState({this.user});
User user = User();
var _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Container(
child: Builder(
builder: (context) => Container(
color: Colors.blueAccent[100],
child: Form(
key: _key,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
"First Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
initialValue: widget.user?.firstName == null ||
widget.user.firstName.isEmpty
? user.firstName
: widget.user.firstName,
validator: (value) {
if (value.isEmpty) {
return "First name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.firstName = value;
});
},
),
),
)
],
),
Row(
children: <Widget>[
Text(
"Last Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Last name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.lastName = value;
});
},
),
),
),
],
),
RaisedButton(
child: Text(
"Save",
),
splashColor: Colors.blueGrey,
onPressed: () {
if (!_key.currentState.validate()) {
return;
}
_key.currentState.save();
widget.onUserAdded(
new User(
firstName: user.firstName,
lastName: user.lastName,
),
);
},
)
],
),
),
),
),
),
);
}
}
Here is my list widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudList extends StatefulWidget {
CrudList({Key key, this.users, this.onUserSelected}) : super(key: key);
final List<User> users;
final SelectUser onUserSelected;
_CrudListState createState() => _CrudListState();
}
class _CrudListState extends State<CrudList> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: ListView.builder(
itemCount: widget.users?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var user = widget.users[index];
return ListTile(
key: Key(index.toString()),
title: Center(
child: Text(
"${user.firstName} ${user.lastName}",
style: TextStyle(color: Colors.white),
),
),
onTap: () {
print("${widget.users[index]} $index");
widget.onUserSelected(widget.users[index]);
},
);
},
),
);
}
}
typedef void SelectUser(User user);
And just for further context, here is my user service, responsible for adding the objects to the stream, and using the stream builder within rxdart to notify of state changes.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:rxdart/rxdart.dart';
class UserService {
User _editedUser = User();
List<User> _users = <User>[];
BehaviorSubject<User> _userSubject;
BehaviorSubject<List<User>> _usersSubject;
UserService(this._editedUser) {
_userSubject = BehaviorSubject<User>.seeded(_editedUser);
_usersSubject = BehaviorSubject<List<User>>.seeded(_users);
}
Observable<List<User>> get usersObservable => _usersSubject.stream;
Observable<User> get userObservable => _userSubject.stream;
addUser(User user) {
_users.add(user);
_usersSubject.add(_users);
}
dispose() {
_userSubject.close();
_usersSubject.close();
}
void userSelected(User user) {
_editedUser = user;
_userSubject.add(_editedUser);
}
}
What am I missing? It looks like my widget rebuilds, and tries to set the initial value in the form when I tap the user in the list widget. But the actual field doesn't get updated and I'm not sure why.
I'd appreciate any documentation or articles on how to better approach data and state management between sibling widgets within the flutter framework.
Here's a similar use case that I tried to implement locally. What I'm doing here is I generate TextFormFields dynamically and assign TextEditingController.
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
Clicking a ListView item updates the text of the currently selected TextFormField.
InkWell(
onTap: () {
debugPrint('Selected $index!');
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
Complete sample
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.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController? _selectedField;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(flex: 1, child: textField(3)),
Expanded(flex: 1, child: listItems()),
],
),
),
),
);
}
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
debugPrint('Current Controller: $textEditingController');
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
ListView listItems() {
return ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
},
);
}
}
Demo

Flutter : problem re-rendering ListView using Stream Builder in bottom Navbar

I have a problem when displaying data to list view using the stream builder, my list view is always re-reload, when the tab is active.
i am implementing AutomaticKeepAliveClientMixin, but that is still not working.
here my code:
Home Bottom Nav :
https://pastebin.com/B9qf0zZR
List View index:
import 'package:eservice_f/src/blocs/listDataBloc.dart';
import 'package:eservice_f/src/models/listModel.dart';
import 'package:eservice_f/utils/layout.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ServiceIndex extends StatefulWidget {
ServiceIndex({Key key}) : super(key: key);
_ServiceIndexState createState() => _ServiceIndexState();
}
class _ServiceIndexState extends State<ServiceIndex> with AutomaticKeepAliveClientMixin<ServiceIndex>{
ListDataBloc _bloc;
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
#override
void initState() {
// TODO: implement initState
super.initState();
_bloc = ListDataBloc();
_bloc.showAllData();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
//bloclist.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
child: StreamBuilder(
stream: _bloc.allData,
builder: (context, AsyncSnapshot<ListData> snapshot) {
print(snapshot.data);
if (snapshot.hasData) {
return Container(
color: Colors.white,
child: Center(
child: getServiceList(context, snapshot),
),
);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Container(
color: Colors.white,
child: Center(child: CupertinoActivityIndicator()));
}),
);
}
Widget getServiceList(
BuildContext context, AsyncSnapshot<ListData> snapshot) {
SizeConfig().init(context);
var _list_data = snapshot.data.data;
return ListView.builder(
itemCount: _list_data.length,
itemBuilder: (BuildContext contex, int index) {
return Column(
children: <Widget>[
ListTile(
onTap: () {
print("List Tapped");
},
leading: Column(
children: <Widget>[
Icon(
Icons.check_circle_outline,
size: SizeConfig.blocHorizontal * 10,
),
],
),
title: Text("SBG/LK/20180814"),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('SusSystem Update Training MEX',
overflow: TextOverflow.ellipsis),
Text(
'So basically you building a Facebook/Instagram like application, where user logs in, scrolls through their feed, stalks through different profiles, and when done, wants to log out of the app',
overflow: TextOverflow.ellipsis),
],
),
trailing: (_list_data[index].activityStatus == "1")
? Text("Draft")
: Text("Confirm"),
),
Divider(
height: 1.0,
),
],
);
});
}
}
Bloc:
import 'package:eservice_f/src/models/listModel.dart';
import 'package:eservice_f/src/resources/repository.dart';
import 'package:rxdart/rxdart.dart';
class ListDataBloc{
final _repository = Repository();
final _fetcher = PublishSubject<ListData>();
Observable<ListData> get allData => _fetcher.stream;
showAllData() async{
ListData datas = await _repository.fetchAll();
_fetcher.sink.add(datas);
}
dispose(){
_fetcher.close();
}
}
//initial Bloc
//final bloclist = ListDataBloc();