Related
Whenever I change the page and come back, the api is reloaded. I have tried many suggestions. I would be glad if you help.
Here are the methods I tried :
How to avoid reloading data every time navigating to page
How to parse JSON only once in Flutter
Flutter Switching to Tab Reloads Widgets and runs FutureBuilder
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'models/Word.dart';
class WordListPage extends StatefulWidget {
WordListPage(Key k) : super(key: k);
#override
_WordListPageState createState() => _WordListPageState();
}
class _WordListPageState extends State<WordListPage> {
Future<List<Word>> data;
bool isSearching = false;
TextEditingController myController = TextEditingController();
List<Word> _words = List<Word>();
List<Word> _wordsForDisplay = List<Word>();
var keyListPage = PageStorageKey('list_page_key');
Future<List<Word>> getWord() async {
var response = await http.get("myAPIurl");
var _words = List<Word>();
_words = (json.decode(utf8.decode(response.bodyBytes)) as List)
.map((singleWordMap) => Word.fromJsonMap(singleWordMap))
.toList();
return _words;
}
#override
void initState() {
getWord().then((value) {
setState(() {
_words.addAll(value);
_wordsForDisplay = _words;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: myFutureBuilder(),
appBar: AppBar(
leading: Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: 'Total',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 10,
color: Colors.white,
),
),
TextSpan(
text: '\n${_words.length.toString()}',
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 12,
),
),
TextSpan(
text: '\nLetter',
style: TextStyle(
decoration: TextDecoration.none,
color: Colors.white,
fontSize: 10,
),
),
],
),
),
),
centerTitle: true,
title: !isSearching
? Text('My Title')
: TextField(
autofocus: true,
style: TextStyle(color: Colors.white),
controller: myController,
onChanged: (value) {
value = value.toLowerCase();
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(value);
},
).toList();
},
);
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordPronounce = word.pronunciation.toLowerCase();
return wordPronounce.contains(value);
},
).toList();
},
);
},
decoration: InputDecoration(
isCollapsed: true,
icon: Icon(
Icons.menu_book,
color: Colors.white,
),
hintText: 'Search',
hintStyle: TextStyle(color: Colors.white),
),
),
actions: [
isSearching
? IconButton(
icon: Icon(Icons.cancel_outlined),
onPressed: () {
setState(
() {
this.isSearching = false;
myController.clear();
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(wordTitle);
},
).toList();
},
);
},
)
: IconButton(
icon: Icon(Icons.search_sharp),
onPressed: () {
setState(
() {
this.isSearching = true;
},
);
},
),
],
),
);
}
FutureBuilder<List<Word>> myFutureBuilder() {
return FutureBuilder(
future: getWord(),
builder: (context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData) {
return myWordListView(snapshot);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
return ListView.builder(
itemCount: _wordsForDisplay.length,
itemBuilder: (context, index) {
return ExpansionTile(
title: Text(
_wordsForDisplay[index].word,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
),
subtitle: Text(
snapshot.data[index].pronunciation[0].toUpperCase() +
snapshot.data[index].pronunciation.substring(1),
),
leading: CircleAvatar(
child: Text(snapshot.data[index].word[0]),
),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 7.0, horizontal: 19.0),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: snapshot.data[index].word + ' : ',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: snapshot.data[index].meaning),
],
),
),
),
),
],
),
],
);
},
);
}
}
When you navigate and return back, the build method is called. Here you have "myFutureBuilder" placed as the body widget of Scaffold, thus this code get executed, within it "getWord" method is called, an it fetches data from the api everytime.
I suggest you to remove "myFutureBuider" and use "myWirdListView" directly as the body of the scaffold. Change myWordListView(List<Word> listOfWord) to use the word list you have already fetched in the initState() .
You need to to separate your api call from your ui. That way your api will only get called when you want it to. I recommend using some kind of external state management library, such as Provider, BLoC, or RxDart. Flutter will rebuild a widget anytime it wants to, beyond when you trigger it.
you passed getWord() as a function to you future builder so each time the widget get rebuilt, it is called.
To solve that, declare a variable Future<Word> getWordFuture ; , Inside initState assign getWordFuture = getWord(); and use getWordFuture in the Future builder.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'models/Word.dart';
class WordListPage extends StatefulWidget {
WordListPage(Key k) : super(key: k);
#override
_WordListPageState createState() => _WordListPageState();
}
class _WordListPageState extends State<WordListPage> {
Future<List<Word>> data;
bool isSearching = false;
TextEditingController myController = TextEditingController();
List<Word> _words = List<Word>();
List<Word> _wordsForDisplay = List<Word>();
var keyListPage = PageStorageKey('list_page_key');
Future<List<Word>> getWordFuture = Future<List<Word>> ; //1
Future<List<Word>> getWord() async {
var response = await http.get("myAPIurl");
var _words = List<Word>();
_words = (json.decode(utf8.decode(response.bodyBytes)) as List)
.map((singleWordMap) => Word.fromJsonMap(singleWordMap))
.toList();
return _words;
}
#override
void initState() {
getWordFuture = getWord(); //2
getWord().then((value) {
setState(() {
_words.addAll(value);
_wordsForDisplay = _words;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: myFutureBuilder(),
appBar: AppBar(
leading: Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: 'Total',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 10,
color: Colors.white,
),
),
TextSpan(
text: '\n${_words.length.toString()}',
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 12,
),
),
TextSpan(
text: '\nLetter',
style: TextStyle(
decoration: TextDecoration.none,
color: Colors.white,
fontSize: 10,
),
),
],
),
),
),
centerTitle: true,
title: !isSearching
? Text('My Title')
: TextField(
autofocus: true,
style: TextStyle(color: Colors.white),
controller: myController,
onChanged: (value) {
value = value.toLowerCase();
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(value);
},
).toList();
},
);
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordPronounce = word.pronunciation.toLowerCase();
return wordPronounce.contains(value);
},
).toList();
},
);
},
decoration: InputDecoration(
isCollapsed: true,
icon: Icon(
Icons.menu_book,
color: Colors.white,
),
hintText: 'Search',
hintStyle: TextStyle(color: Colors.white),
),
),
actions: [
isSearching
? IconButton(
icon: Icon(Icons.cancel_outlined),
onPressed: () {
setState(
() {
this.isSearching = false;
myController.clear();
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(wordTitle);
},
).toList();
},
);
},
)
: IconButton(
icon: Icon(Icons.search_sharp),
onPressed: () {
setState(
() {
this.isSearching = true;
},
);
},
),
],
),
);
}
FutureBuilder<List<Word>> myFutureBuilder() {
return FutureBuilder(
//future: getWord(),
future:getWordFuture,
builder: (context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData) {
return myWordListView(snapshot);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
return ListView.builder(
itemCount: _wordsForDisplay.length,
itemBuilder: (context, index) {
return ExpansionTile(
title: Text(
_wordsForDisplay[index].word,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
),
subtitle: Text(
snapshot.data[index].pronunciation[0].toUpperCase() +
snapshot.data[index].pronunciation.substring(1),
),
leading: CircleAvatar(
child: Text(snapshot.data[index].word[0]),
),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 7.0, horizontal: 19.0),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: snapshot.data[index].word + ' : ',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: snapshot.data[index].meaning),
],
),
),
),
),
],
),
],
);
},
);
}
}
I am using the flutter_staggered_grid_view package (https://github.com/letsar/flutter_staggered_grid_view) to have a feed of images. I want to implement something where first of all I can fetch say 30 images on initial screen load then when I reach the end and the scroll listener fires fetch more from the api. How do I change the below to do this? Can anyone give me any direction on docs etc. I think fundamentally this package is using gridview underneath but I am new to flutter so I'm unsure.
I just want an infinite scroll feed of images from an api.
class HomeScreen extends StatefulWidget {
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomeScreen> {
final scrollController = ScrollController();
#override
void initState() {
super.initState();
scrollController.addListener(() {
if (scrollController.position.maxScrollExtent ==
scrollController.offset) {
// Get more
print('End of screen');
}
});
}
List<String> imageList = [
'https://cdn.pixabay.com/photo/2020/12/15/16/25/clock-5834193__340.jpg',
'https://cdn.pixabay.com/photo/2020/09/18/19/31/laptop-5582775_960_720.jpg',
'https://media.istockphoto.com/photos/woman-kayaking-in-fjord-in-norway-picture-id1059380230?b=1&k=6&m=1059380230&s=170667a&w=0&h=kA_A_XrhZJjw2bo5jIJ7089-VktFK0h0I4OWDqaac0c=',
'https://cdn.pixabay.com/photo/2019/11/05/00/53/cellular-4602489_960_720.jpg',
'https://cdn.pixabay.com/photo/2017/02/12/10/29/christmas-2059698_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/01/29/17/09/snowboard-4803050_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/02/06/20/01/university-library-4825366_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/11/22/17/28/cat-5767334_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/12/13/16/22/snow-5828736_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/12/15/16/25/clock-5834193__340.jpg',
'https://cdn.pixabay.com/photo/2020/09/18/19/31/laptop-5582775_960_720.jpg',
];
#override
Widget build(BuildContext context) {
/// If you set your home screen as first screen make sure call [SizeConfig().init(context)]
SizeConfig().init(context);
return Scaffold(
body: Platform.isIOS
? Container(
margin: EdgeInsets.only(left: 12, right: 12, top: 5),
child: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
slivers: <Widget>[
SliverAppBar(
floating: true,
title: SvgPicture.asset(
"assets/images/logo-dark.svg",
height: getProportionateScreenWidth(40),
),
actions: [
// Filter Button
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilterScreen(),
),
),
child: Text(
"Filter",
style: Theme.of(context).textTheme.bodyText1,
),
),
],
),
CupertinoSliverRefreshControl(
onRefresh: () async {
await Future.delayed(Duration(seconds: 2));
},
),
SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(1, index.isEven ? 1.2 : 1.8);
},
)
],
),
)
: RefreshIndicator(
color: kMainColor,
displacement: 120,
onRefresh: () async {
await Future.delayed(Duration(seconds: 2));
},
child: Container(
margin: EdgeInsets.only(left: 12, right: 12, top: 5),
child: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
slivers: <Widget>[
SliverAppBar(
floating: true,
title: SvgPicture.asset(
"assets/images/logo-dark.svg",
height: getProportionateScreenWidth(40),
),
actions: [
// Filter Button
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilterScreen(),
),
),
child: Text(
"Filter",
style: Theme.of(context).textTheme.bodyText1,
),
),
],
),
SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(1, index.isEven ? 1.2 : 1.8);
},
)
],
),
),
),
);
}
}
You need to add the ScrollController for the scrolling detection at the bottom for the ListView and GridView. As you need the GridView i have created the ScrollController listner and added to the GridView's contollerfor the detection of the scroll. I have created the demo of it , please check it once. At first time it load the 10 items and when list comes to the bottom then it add more 10 items in it.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeState();
}
}
class HomeState extends State<HomeScreen> {
List dataList = new List<int>();
bool isLoading = false;
int pageCount = 1;
ScrollController _scrollController;
#override
void initState() {
super.initState();
////LOADING FIRST DATA
addItemIntoLisT(1);
_scrollController = new ScrollController(initialScrollOffset: 5.0)
..addListener(_scrollListener);
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gridview',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.red,
accentColor: Color(0xFFFEF9EB),
),
home: Scaffold(
appBar: new AppBar(),
body: GridView.count(
controller: _scrollController,
scrollDirection: Axis.vertical,
crossAxisCount: 2,
mainAxisSpacing: 10.0,
physics: const AlwaysScrollableScrollPhysics(),
children: dataList.map((value) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.2,
margin: EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Text("Item ${value}"),
);
}).toList(),
)));
}
//// ADDING THE SCROLL LISTINER
_scrollListener() {
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
print("comes to bottom $isLoading");
isLoading = true;
if (isLoading) {
print("RUNNING LOAD MORE");
pageCount = pageCount + 1;
addItemIntoLisT(pageCount);
}
});
}
}
////ADDING DATA INTO ARRAYLIST
void addItemIntoLisT(var pageCount) {
for (int i = (pageCount * 10) - 10; i < pageCount * 10; i++) {
dataList.add(i);
isLoading = false;
}
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
https://i.stack.imgur.com/7hcDc.gif
hello i am new to flutter and i m using update profile api in my profile api i describe all parameter and image base 64 also now what is going on ! suppose alredy is image showing on my profile pic and i never pic image from my camera and device My "Profile Pic" parameter showing null when i call it...! and when i picke image from my device it showing base 64...! what i want is if there is alredy image showing then profile api function i want success because there is alredy image shown ... now u suggest me when i call Profile api function on my Button wht i need to pass
PickedFile _imageFile;
final ImagePicker _picker = ImagePicker();
String img64;
void takePhoto(ImageSource source) async {
final pickedFile = await _picker.getImage(source: source);
setState(() {
_imageFile = pickedFile;
final bytes = Io.File(_imageFile.path).readAsBytesSync();
img64 = base64Encode(bytes);
print(img64.substring(0, 100));
});
}
Profile(String FirstnName,String Lastname,String Email,String DOB,String
Anniversary) async {
sharedPreferences = await SharedPreferences.getInstance();
ValidateCustomer();
Map data =
{
"AccessToken": sharedPreferences.getString("AccessToken"),
"CustomerId": sharedPreferences.getInt("CustomerId"),
"FirstName": FirstnName ,
"LastName": Lastname,
"Email": Email,
"DOB":DOB,
"AnniversaryDate": Anniversary,
"ProfilePicture" : img64
};
print(data);
final http.Response response = await http.post(
Constants.CUSTUMER_WEBSERVICE_UPDATEPROF_URL,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(data),
);
var jsonResponse;
if (response.statusCode == 200)
{
print(sharedPreferences.setString("FirstName", FirstnName));
jsonResponse = json.decode(response.body);
sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString("FirstName", FirstnName);
sharedPreferences.setString("ProfilePicture",img64);
print("Response status : ${response.statusCode}");
print("Response status : ${response.body}");
print(sharedPreferences);
if(jsonResponse != null && ! jsonResponse.containsKey("Error")){
setState(() {
_isLoading = false;
});
print(sharedPreferences);
Navigator.of(context).push(
MaterialPageRoute(builder: (context)=>
ShelfScreen())
);
}
else{
setState(() {
_isLoading = false;
});
print("Response status : ${response.body}");
}
}
if (response.statusCode == 404){
print("Response status : ${response.statusCode}");
print("Response status : ${response.body}");
}
}
Future<UserUpdate>ProfileUpadte() async {
sharedPreferences = await SharedPreferences.getInstance();
ValidateCustomer();
Map data =
{
"AccessToken": sharedPreferences.getString("AccessToken"),
"CustomerId": sharedPreferences.getInt("CustomerId"),
};
print(data);
final http.Response response = await http.post(
"http://api.pgapp.in/v1/userdetails",
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(data),
);
if (response.statusCode == 200)
{
print("Response status : ${response.statusCode}");
print("Response status : ${response.body}");
var jsonResponse = json.decode(response.body);
if(jsonResponse != null && ! jsonResponse.containsKey("Error")){
sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString("ProfilePicture",
jsonResponse["ProfilePicture"]);
sharedPreferences.setString("FirstName", jsonResponse["FirstName"]);
print(sharedPreferences);
}
else{
setState(() {
_isLoading = false;
});
print("Response status : ${response.body}");
}
return UserUpdate.fromJson(json.decode(response.body));
}
else {
// If the server did not return a 201 CREATED response,
// then throw an exception.
throw Exception('Failed to load data');
}
}
void nextField ({String value,FocusNode focusNode}){
if (value.isNotEmpty){
focusNode.requestFocus();
}
}
AnimationController _controller;
Animation _animation;
FocusNode _focusNode1 = FocusNode();
bool _isLoading = false;
String _Firstnm;
String _Lastnm;
String _Mobno;
String _Email;
String _Dob;
String _Anniversary;
DateTime _selectedDate;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode Firstname;
FocusNode LastndName;
FocusNode Email;
FocusNode DOB;
FocusNode Anniversary;
Future<UserUpdate> _futureProfileupdate;
#override
void initState() {
// TODO: implement initState
super.initState();
Firstname = FocusNode();
LastndName = FocusNode();
Email = FocusNode();
DOB = FocusNode();
Anniversary = FocusNode();
_futureProfileupdate = ProfileUpadte();
}
void _showPicker(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Container(
child: new Wrap(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.photo_library),
title: new Text('Photo Library'),
onTap: () {
takePhoto(ImageSource.gallery);
Navigator.of(context).pop();
}),
new ListTile(
leading: new Icon(Icons.photo_camera),
title: new Text('Camera'),
onTap: () {
takePhoto(ImageSource.camera);
Navigator.of(context).pop();
},
),
],
),
),
);
}
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
body: (_isLoading) ? Center(child: CircularProgressIndicator()) :
FutureBuilder<UserUpdate>(
future: _futureProfileupdate,
builder: (context, snapshot) {
if (snapshot.hasData) {
TextEditingController _textEditingControllerFirstNamee =
TextEditingController(text: snapshot.data.firstName);
TextEditingController _textEditingControllerLastnamee = TextEditingController(text: snapshot.data.lastName);
TextEditingController _textEditingControllerEmaill = TextEditingController(text: snapshot.data.email);
TextEditingController _textEditingControllerDOBb = TextEditingController(text: snapshot.data.dob);
TextEditingController _textEditingControllerAnniversaryy = TextEditingController(text: snapshot.data.anniversaryDate);
_selectDate(BuildContext context) async {
DateTime newSelectedDate = await showDatePicker(
context: context,
initialDate: _selectedDate != null ? _selectedDate : DateTime.now(),
firstDate: DateTime(1920),
lastDate: DateTime(2040),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
primary: Colors.deepPurple,
onPrimary: Colors.white,
surface: Colors.blueGrey,
onSurface: Colors.yellow,
),
dialogBackgroundColor: Colors.blue[500],
),
child: child,
);
});
if (newSelectedDate != null) {
_selectedDate = newSelectedDate;
_textEditingControllerDOBb
..text = DateFormat.yMMMd().format(_selectedDate)
..selection = TextSelection.fromPosition(TextPosition(
offset: _textEditingControllerDOBb.text.length,
affinity: TextAffinity.upstream));
}
}
_selectDate2(BuildContext context) async {
DateTime newSelectedDate = await showDatePicker(
context: context,
initialDate: _selectedDate != null ? _selectedDate : DateTime.now(),
firstDate: DateTime(1920),
lastDate: DateTime(2040),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
primary: Colors.deepPurple,
onPrimary: Colors.white,
surface: Colors.blueGrey,
onSurface: Colors.yellow,
),
dialogBackgroundColor: Colors.blue[500],
),
child: child,
);
});
if (newSelectedDate != null) {
_selectedDate = newSelectedDate;
_textEditingControllerAnniversaryy
..text = DateFormat.yMMMd().format(_selectedDate)
..selection = TextSelection.fromPosition(TextPosition(
offset: _textEditingControllerAnniversaryy.text.length,
affinity: TextAffinity.upstream));
}
}
return Form(
key: _formKey,
child: Stack(
children: <Widget>[
Positioned(
left: 20,
top: 40,
child: Center(
child: Image.asset(
'assets/images/logo.png', height: 50, width: 50,),
)
),
Positioned(
left: 60,
right: 60,
top: 40,
child: Center(
child: Image.asset(
'assets/images/Textimage.png', height: 50,
width: 170,),
)
),
Positioned(
right: 20,
top: 40,
child: Center(
child: IconButton(
icon: Icon(Icons.exit_to_app, size: 30,
color: Color(0xfff58634),),
onPressed: () async {
showAboutDialog(context);
},
)
)
),
Positioned(
left: 70,
right: 70,
top: 110,
child: Center(
child: CircleAvatar(
backgroundImage: _imageFile == null ? NetworkImage(
snapshot.data.profilePicture) : FileImage(Io.File(_imageFile.path)),
backgroundColor: Colors.grey,
maxRadius: 50,
)
)
),
Positioned(
left: 100,
right: 70,
top: 170,
child: InkWell(
onTap: (){
_showPicker(context);
},
child: Icon(
Icons.camera_alt,
color: Colors.white,
size: 30,
),
),
),
Positioned(
left: 30,
right: 30,
top: 250,
child: Container(
height: 480,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 5),
child: SizedBox(
height: 70,
child: TextFormField(
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
textInputAction: TextInputAction.next,
focusNode: Firstname,
onFieldSubmitted: (value) {
nextField(
value: value, focusNode: LastndName);
},
controller: _textEditingControllerFirstNamee,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634)
)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0)
),
labelText: "First Name",
labelStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
hintText: "First Name",
hintStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634),
)
),
),
validator: (String value) {
if (value.isEmpty) {
return 'First Name is Required';
}
return null;
},
onSaved: (String value) {
_Firstnm = value;
},
),
),
),
Padding(
padding: const EdgeInsets.only(top: 5),
child: SizedBox(
height: 70,
child: TextFormField(
keyboardType: TextInputType.text,
textCapitalization:
TextCapitalization.sentences,
focusNode: LastndName,
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
nextField(value: value, focusNode:
Email);
},
controller:
_textEditingControllerLastnamee,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634)
)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0)
),
labelText: 'Last Name',
labelStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
hintText: "Last Name",
hintStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634),
)
),
),
validator: (String value) {
if (value.isEmpty) {
return 'First Name is Required';
}
return null;
},
onSaved: (String value) {
_Lastnm = value;
},
),
),
),
Padding(
padding: const EdgeInsets.only(top: 5),
child: SizedBox(
height: 70,
child: TextFormField(
focusNode: Email,
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
nextField(value: value, focusNode: DOB);
},
controller: _textEditingControllerEmaill,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634)
)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0)
),
labelText: 'Email',
labelStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
hintText: "Email",
hintStyle: GoogleFonts.nunito(
color: const Color(0xfff58634)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
5.0),
borderSide: BorderSide(
color: const Color(0x3df58634),
)
),
),
validator: (value) => EmailValidator.validate(value) ? null : "Please enter a valid email",
onSaved: (String value) {
_Email = value;
},
),
),
),
Padding(
padding: EdgeInsets.only(top: 45),
child: SizedBox(
width: 360,
height: 45,
child: RaisedButton(
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
setState(() {
_isLoading = true;
});
Profile(
_textEditingControllerFirstNamee.text,
_textEditingControllerLastnamee.text,
_textEditingControllerEmaill.text,
_textEditingControllerDOBb.text,
_textEditingControllerAnniversaryy
.text,);
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ShelfScreen()));
},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(
5.0)),
color: const Color(0xfff58634),
child: Text(
"Update",
style: GoogleFonts.nunito(
color: const Color(0xffffffff),
fontSize: 20
),
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 120),
child: Text(".", style: TextStyle(
color: Colors.transparent, fontSize: 1,
),),
)
],
),
),
),
),
],
),
);
} else {
return CircularProgressIndicator();
);
}
}
)
);
}
}
class AlwaysDisabledFocusNode extends FocusNode {
#override
bool get hasFocus => false;
}
i am trying to implement login functionality using bloc pattern in flutter. So I want to navigate to main page after authentication is successful. from loginBloc.dart I will get the status inside the streamBuilder in login.dart when the status is success I want to navigate to main.dart but I cant able to understand how to call the main.dart inside the streamBuilder in login.dart.
login.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hotelorders/bloc/LoginBloc.dart';
import 'package:hotelorders/screens/Home.dart';
class Login extends StatefulWidget
{
#override
State<StatefulWidget> createState() {
return LoginState();
}
}
class LoginState extends State<Login>
{
final LoginBloc _loginBloc = LoginBloc();
String _email,_password;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
void dispose()
{
_loginBloc.dispose();
super.dispose();
}
Widget _progressBar()
{
return StreamBuilder<bool>(
stream: _loginBloc.progressStream,
builder: (BuildContext context,AsyncSnapshot<bool> snapShot) {
bool visible;
if(snapShot.data == null)
{
visible = false;
}
else
{
visible = snapShot.data;
}
return Visibility(
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: visible,
child: Container(
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0), //(x,y)
blurRadius: 1.0,
),
],
),
),
Center(
child: CircularProgressIndicator(),
)
],
),
)
)
),
);
}
);
}
Widget _emailTextField()
{
return TextFormField(
decoration: InputDecoration(
labelText: "Email id",
border: OutlineInputBorder(
borderRadius: new BorderRadius.circular(32.0),
)
),
keyboardType: TextInputType.emailAddress,
validator: (String value){
if(value.isEmpty || !RegExp(r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?").hasMatch(value))
{
return "Enter a valid email id";
}
return null;
},
onSaved: (String value){
_email = value;
},
);
}
Widget _passwordTextField()
{
return TextFormField(
decoration: InputDecoration(labelText: "Password",
filled: true,
border: OutlineInputBorder(
borderRadius: new BorderRadius.circular(32.0),
),
fillColor: Colors.white
),
keyboardType: TextInputType.text,
obscureText: true,
validator: (String value){
if(value.isEmpty)
{
return "Enter a valid password";
}
else if(value.length < 8)
{
return "Password is too short";
}
else
{
return null;
}
},
);
}
void _login()
{
if(!_formKey.currentState.validate())
{
return;
}
_formKey.currentState.save();
Map<String,String> map = new Map();
map['email'] = _email;
map['password'] = _password;
_loginBloc.login(map);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
margin: EdgeInsets.fromLTRB(0, 48, 0, 0),
child: Text(
"Take Orders and",
style: TextStyle(color: Theme.of(context).primaryColorDark,fontSize: 20),
),
),
Container(
child: Text(
"Track the Best Selling Items",
style: TextStyle(color: Theme.of(context).primaryColor,fontSize: 16),
),
),
Container(
margin: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: SvgPicture.asset('assets/images/undraw_booking.svg',width: 100.0,height: 280.0,),
),
Container(
margin: EdgeInsets.fromLTRB(16, 0, 0, 0),
child:Row(
children: <Widget>[
Text(
"Login To ",
style: TextStyle(color: Colors.black,fontSize: 20)
),
Text(
"Take orders",
style: TextStyle(color: Theme.of(context).primaryColorDark,fontSize: 20),
)
],
),
),
Container(
margin: EdgeInsets.fromLTRB(16, 8, 16, 0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: _emailTextField(),
),
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: _passwordTextField(),
),
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: Align(
alignment: Alignment.centerLeft,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(32.0)
),
padding: EdgeInsets.fromLTRB(64, 12, 64, 12),
color: Theme.of(context).accentColor,
textColor: Colors.white,
child: Text(
"Login",
),
onPressed: (){
_login();
},
) ,
),
),
StreamBuilder<dynamic>(
stream:_loginBloc.loginStateStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapShot){
if(snapShot.data == "success")
{
// WidgetsBinding.instance.addPostFrameCallback((_){
// Navigator.push(context, MaterialPageRoute(
// builder: (context)=> Home()
// ));
// });
}
else if(snapShot.data != null && snapShot.data != "success")
{
WidgetsBinding.instance.addPostFrameCallback((_) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('${snapShot.data}',
style: TextStyle(color: Colors.black),),
backgroundColor: Color(0xFFe5e5e5),
));
// showDialog(context: context,builder: (BuildContext con){
// return AlertDialog(
// title: Text('${snapShot.data}'),
// );
// });
});
}
return Container();
},
)
],
),
),
)
],
),
)
),
_progressBar(),
],
),
);
}
}
LoginBloc.dart
import 'dart:async';
class LoginBloc
{
// here event comes in and state goes out
//stream conntroller for output
final _loginStateController = StreamController<String>();
//stream controller for input
final _loginEventController = StreamController<Map<String,String>>();
final _progressController = StreamController<bool>();
StreamSink<String> get loginStateSink => _loginStateController.sink;
Stream<String> get loginStateStream => _loginStateController.stream;
Sink<Map<String,String>> get loginEventSink => _loginEventController.sink;
Stream<bool> get progressStream => _progressController.stream;
StreamSink<bool> get progressSink => _progressController.sink;
LoginBloc()
{
progressSink.add(false);
_loginEventController.stream.listen(login);
}
login(Map<String,String> loginDetails)
{
progressSink.add(true);
Timer(Duration(seconds: 3), () {
progressSink.add(false);
loginStateSink.add("success");
});
// when we pass data in sink we get the output from stream
}
void dispose()
{
_loginEventController.close();
_loginStateController.close();
_progressController.close();
}
}
You are almost there. The following should work :
if(snapShot.data == "success")
{
Navigator.push(
context, MaterialPageRoute(builder: (context)=> Home()));
}
Note : WidgetsBinding.instance.addPostFrameCallback is used to get a callback when widget tree is loaded.
I have the following example codes. I have now managed to put the prefixicon and it works fine. I want to move the same icon the suffix meaning on the right hand side but it just does not work but the X symbol it what appears.
Here is a screen shot.
I have added the following lines suffixIcon: IconButton( but it seems not be appearing but the one on the left hand side which is the prefix appears perfectly fine. I cant get the one on the right hand side. What is blocking it from appearing?
Below is my codes.
class MyHomePageState extends State<MyHomePage> {
// Show some different formats.
final formats = {
//InputType.both: DateFormat("EEEE, MMMM d, yyyy 'at' h:mma"),
//InputType.date: DateFormat('dd/MM/yyyy'),
//InputType.time: DateFormat("HH:mm"),
InputType.date: DateFormat("d MMMM yyyy"),
};
//InputType.date: DateFormat('yyyy-MM-dd'),
// Changeable in demo
InputType inputType = InputType.date;
bool editable = true;
DateTime date;
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(appName)),
body: Padding(
padding: EdgeInsets.all(16.0),
child: ListView(
children: <Widget>[
Form(
//key: _myKey,
child: Column(
children : [
new Container(
width: 200.0,
child:new DateTimePickerFormField(
dateOnly: true,
format: formats[inputType],
editable: false,
validator: (val) {
if (val != null) {
return null;
} else {
return 'Date Field is Empty';
}
},
/*decoration: InputDecoration(
border: InputBorder.none,
labelText: 'From',contentPadding: const EdgeInsets.symmetric(horizontal: 20.0)),*/
decoration: InputDecoration(
hintText: 'To',
border: InputBorder.none,
filled: false,
prefixIcon: Icon(
Icons.arrow_drop_down,
color: Colors.blue,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.arrow_drop_down,size: 28),
onPressed: () {
debugPrint('222');
})),
initialValue: DateTime.now().subtract(new Duration(days: 7)), //Add this in your Code.
),
)
]
),
),
RaisedButton(
onPressed: () {
/*if (_myKey.currentState.validate()) {
_myKey.currentState.save();
} else {
}*/ print("check;");
if(emailController.text.isEmpty){
print("TEST;");
//valid = false;
//emailError = "Email can't be blank!";
//openAlertBox();
Toast.show("Empty Date From", context, backgroundColor: Colors.red );
}
else{
print("not empty;");
final f = new DateFormat('yyyy-MM-dd');
final original = new DateFormat('d MMMM yyyy');
print("Format datre is"+emailController.text);
print("Formate date :"+original.parse(emailController.text).toString());
}
},
child: Text('Submit'),
)
],
),
));
I re-created your case by singling out only the TextFormField code you provided and was able to see the dropdown arrow as suffixIcon.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: MediaQuery
.of(context)
.size
.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children : [
new Container(
color: Colors.yellow,
width: 200.0,
child: TextFormField(
decoration: InputDecoration(
hintText: 'To',
border: InputBorder.none,
filled: false,
prefixIcon: Icon(
Icons.arrow_drop_down,
color: Colors.blue,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.arrow_drop_down,size: 28),
onPressed: () {
debugPrint('222');
})),
),
)
]
)
),
)
);
}
}
I see that you used Padding as your body to return Scaffold. Try to replace it with Center or Container
You may have discovered the following but posting just in case...
The 'X' displayed is the reset icon for the date field ie. you use that to clear the field. You can turn it off with DateTimePickerFormField property 'resetIcon: null;' but then the only way to remove a date from the field is to ensure 'editable: true', which is the default but you have overridden it in your code.
Hope that helps.