I'm trying to implement a button animation when on clicked it shrinks and shows the circular progress indicator (while loading) then expands and shows the result of the executed operation (in this case it is login).
The code idea came from this link. The design idea came from this link. Now I implemented this before and it worked exactly as it was supposed to. However when implementing it again here, on button pressed -> the person logs in successfully and the button changes color as per design an all. The only problem is that the button animation does not happen. I tried printing the values of the _loginButtonWidth and can actually see it decreasing and increasing as per design, but visually the width stays the same.
Code:
import 'package:flutter/material.dart';
import 'package:garuda_academy_app/Login/Authentication.dart';
import 'package:garuda_academy_app/Tools/FixedColors.dart';
import 'dart:async';
class LoginPage extends StatefulWidget {
LoginPage({this.auth, this.onLoggedIn});
#override
_LoginPageState createState() => _LoginPageState();
final BaseAuth auth;
final VoidCallback onLoggedIn;
}
class _LoginPageState extends State<LoginPage> with TickerProviderStateMixin {
// for device type
bool _isIos;
// text form field
String _userEmail = "";
String _userPassword = "";
final _formKey = GlobalKey<FormState>();
// for login button
int _loginButtonState = 0;
double _loginButtonWidth = double.maxFinite;
Color _loginButtonColor = primaryColor;
Color _loginButtonOutlineColor = primaryColor;
Color _loginButtonTextColor = secondaryColor;
GlobalKey _loginButtonKey = GlobalKey();
Animation _loginButtonAnimation;
AnimationController _loginButtonController;
Widget _loginButton() {
if (_loginButtonState == 0) {
return Text(
"Log In",
style: TextStyle(
color: _loginButtonTextColor,
fontSize: 20,
),
);
} else if (_loginButtonState == 1) {
return CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(secondaryColor),
);
} else if (_loginButtonState == 2) {
return Icon(
Icons.check,
color: _loginButtonTextColor,
);
} else if (_loginButtonState == 3) {
return Icon(
Icons.close,
color: _loginButtonTextColor,
);
} else if (_loginButtonState == 4) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.check,
color: _loginButtonTextColor,
),
Icon(
Icons.check,
color: transparent,
),
Text(
"Successful",
style: TextStyle(
color: _loginButtonTextColor,
),
),
],
);
} else if (_loginButtonState == 5) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.close,
color: _loginButtonTextColor,
),
Icon(
Icons.close,
color: transparent,
),
Text(
"Unsuccessful",
style: TextStyle(
color: _loginButtonTextColor,
),
),
],
);
}
}
bool _validateLoginAndSave() {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
return true;
}
return false;
}
_animateLoginButton() async {
String userId = "";
String errorMsg = "";
setState(() {
_loginButtonState = 1;
});
// animation
double initialWidth = _loginButtonKey.currentContext.size.width;
_loginButtonController =
AnimationController(duration: Duration(milliseconds: 300), vsync: this)
..addStatusListener((AnimationStatus status) async {
if (status == AnimationStatus.completed) {
// firebase signin
try {
userId = await widget.auth.signIn(_userEmail, _userPassword);
} catch (e) {
setState(() {
errorMsg = _isIos ? e.details : e.message;
print(errorMsg);
});
}
// loading timer
Timer(Duration(seconds: 1), () {
// set login state
_loginButtonState =
(userId.length > 0 && userId != null) ? 2 : 3;
// change colors
if (_loginButtonState == 2) {
_loginButtonColor = secondaryColor;
_loginButtonOutlineColor = successfulColor;
_loginButtonTextColor = successfulColor;
} else if (_loginButtonState == 3) {
_loginButtonColor = secondaryColor;
_loginButtonOutlineColor = unsuccessfulColor;
_loginButtonTextColor = unsuccessfulColor;
}
_loginButtonController.reverse();
});
} else if (status == AnimationStatus.dismissed) {
if (_loginButtonState == 2) {
_loginButtonState = 4;
} else if (_loginButtonState == 3) {
_loginButtonState = 5;
}
// minimal time before it is done
Timer(Duration(seconds: 1), () {
setState(() {
if (_loginButtonState == 4) widget.onLoggedIn();
// reset state
_loginButtonState = 0;
// reset colors
_loginButtonColor = primaryColor;
_loginButtonOutlineColor = primaryColor;
_loginButtonTextColor = secondaryColor;
});
});
}
});
_loginButtonAnimation =
Tween(begin: 0.0, end: 1.0).animate(_loginButtonController)
..addListener(() {
setState(() {
_loginButtonWidth = initialWidth -
((initialWidth - 80.0) * _loginButtonAnimation.value);
});
print("initial: " + initialWidth.toString());
print("current: " + _loginButtonWidth.toString());
});
_loginButtonController.forward();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
_isIos = Theme.of(context).platform == TargetPlatform.iOS;
return Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(
child: Center(
child: Theme(
data: ThemeData(primaryColor: primaryColor),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(40),
child: Text(
"Log in to continue",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: primaryColor,
),
),
),
Padding(
padding: EdgeInsets.only(bottom: 20, left: 40, right: 40),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
style: TextStyle(
fontSize: 20,
),
decoration: InputDecoration(
labelText: "Email Address",
labelStyle: TextStyle(fontSize: 20),
),
validator: (value) =>
value.isEmpty ? "Email cannot be empty" : null,
onSaved: (value) => _userEmail = value,
),
),
Padding(
padding: EdgeInsets.only(bottom: 20, left: 40, right: 40),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
obscureText: true,
style: TextStyle(
fontSize: 20,
),
decoration: InputDecoration(
labelText: "Password",
labelStyle: TextStyle(fontSize: 20),
),
validator: (value) =>
value.isEmpty ? "Password cannot be empty" : null,
onSaved: (value) => _userPassword = value,
),
),
Padding(
padding: EdgeInsets.only(bottom: 50, left: 40, right: 40),
child: Container(
height: 60,
width: _loginButtonWidth,
child: PhysicalModel(
color: transparent,
borderRadius: BorderRadius.circular(10.0),
child: RaisedButton(
elevation: 8.0,
color: _loginButtonColor,
key: _loginButtonKey,
shape: OutlineInputBorder(
borderSide: BorderSide(
color: _loginButtonOutlineColor,
),
borderRadius: BorderRadius.circular(10.0),
),
child: _loginButton(),
onPressed: () {
setState(() {
if (_loginButtonState == 0 &&
_validateLoginAndSave()) {
_animateLoginButton();
}
});
},
),
),
),
),
],
),
),
),
),
),
);
}
}
Button width stays the same when it is supposed to shrink:
Easy fix, add a Center or Align widget as a parent of your Container button.
Padding(
padding: EdgeInsets.only(bottom: 50, left: 40, right: 40),
child: Center(
child: Container(
height: 60,
width: _loginButtonWidth,
To get more info check Layout Behavior
Related
I'm trying to display a result item following the user input in the text field, but I receive all the items. There were some methods I tried, but they didn't work and I encountered some errors.
here is my source code
import 'dart:convert';
import 'package:ebook_flutter_app/constant.dart';
import 'package:ebook_flutter_app/model/image.dart';
import 'package:ebook_flutter_app/model/text_value.dart';
import 'package:ebook_flutter_app/screens/show_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
import '../widgets/showImage.dart';
class SearchScreen extends StatefulWidget {
const SearchScreen({Key? key}) : super(key: key);
#override
SearchScreenState createState() => SearchScreenState();
}
class SearchScreenState extends State<SearchScreen> {
List textValues = [];
List original = [];
List result = [];
TextEditingController txtQuery = TextEditingController();
List<TextValue> textValueList = [];
List<MyImage> myImageList = [];
List<TextValue> getCatList(List<TextValue> inputList, String query) {
List<TextValue> outputList =
inputList.where((item) => item.title == query).toList();
//textValueList = outputList;
//var myList = outputList;
return outputList;
}
List<MyImage> getImageList(List<MyImage> inputList, String query) {
List<MyImage> outputList =
inputList.where((o) => o.id_num!.toString() == query).toList();
// myImageList = outputList;
return outputList;
}
#override
void initState() {
super.initState();
txtQuery.addListener(() {
if (isNumeric(txtQuery.text) == true) {
loadImage();
searchById(txtQuery.text);
print('I\'m using search option for loading Image.... ');
} else {
loadData();
search(txtQuery.text);
print('I\'m using search option for loading Data....');
}
});
}
void loadData() async {
String jsonStr = await rootBundle.loadString('assets/db/text_value.json');
var json = jsonDecode(jsonStr);
textValues = json;
original = json;
setState(() {});
}
void loadImage() async {
String jsonStr = await rootBundle.loadString('assets/db/image_db.json');
var json = jsonDecode(jsonStr);
textValues = json;
original = json;
print('load Image is running....');
setState(() {});
}
void search(String query) {
if (query.isEmpty) {
textValues = original;
setState(() {});
return;
}
query = query.toLowerCase();
print(query);
//List result = [];
textValues.forEach((element) {
var name = element["name"].toString().toLowerCase();
var description = element["description"].toString().toLowerCase();
if (name.contains(query) || description.contains(query)) {
result.add(element);
// textValueList.add(element);
// print('textValueList is $textValueList');
}
});
textValues = result;
setState(() {});
}
void searchById(String query1) {
if (query1.isEmpty) {
textValues = original;
print('query1 is .....$query1');
setState(() {});
return;
}
print('query1 is $query1');
//List result = [];
textValues.forEach((element) {
var id_num = element["id_num"].toString();
var img_num = element["img_num"].toString();
if (id_num.contains(query1)) {
result.add(element);
// myImageList.add(element);
// print('mYImageList is $myImageList');
print('result is......$result');
}
});
textValues = result;
print('textValues is .....$textValues');
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(2),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: txtQuery,
onChanged: (value) {
setState(() {});
},
textDirection: TextDirection.rtl,
decoration: InputDecoration(
hintText: "جست وجو...",
hintTextDirection: TextDirection.rtl,
hintStyle: TextStyle(
color: Colors.black,
fontSize: 18,
fontFamily: 'iran-sans-ds',
decoration: TextDecoration.none,
fontStyle: FontStyle.italic,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
prefixIcon: const Icon(Icons.search),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
txtQuery.text = '';
txtQuery.clear();
},
),
),
keyboardType: TextInputType.text,
),
],
),
),
txtQuery.text.isEmpty
? Container()
: Expanded(
child: ListView.builder(
itemCount: textValues.length,
// isNumeric(txtQuery.text) == true
// ? getImageList(myImageList, txtQuery.text).length
// : getCatList(textValueList, txtQuery.text).length,
itemBuilder: (context, index) {
var textVal = textValues[index];
String description = textVal['description'] ??
'we don\'t have description......';
var id_num = textVal['id_num'].toString() ??
'we don\'t have id_num......';
var img_num = textVal['img_num'].toString() ??
'we don\'t have img_num........... ';
print('id_num is ....$id_num'
' img_num is.....$img_num');
return Card(
margin:
const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
color: Colors.blue[50],
child: Theme(
data: Theme.of(context)
.copyWith(dividerColor: Colors.transparent),
child: InkWell(
onTap: (() => pushNewScreen(
context,
screen: isNumeric(id_num) == false
? ShowItem(
name: textVal['name'],
description:
textVal['description'],
)
: ShowImage(
title: id_num,
image: Myasset(img_num),
),
withNavBar:
true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation:
PageTransitionAnimation.slideRight,
)),
child: ExpansionTile(
title: Text(
isNumeric(id_num) == false
? textVal['name']
: id_num,
textDirection: TextDirection.rtl,
style: const TextStyle(
fontSize: 20.0, color: Colors.black54),
),
childrenPadding: const EdgeInsets.only(
bottom: 20.0,
right: 20.0,
left: 20.0,
top: 5.0),
children: [
isNumeric(id_num) == false
? Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
const Text(
'بیشتر',
textDirection:
TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.blue,
fontWeight:
FontWeight.bold),
),
Text(
'${description.substring(0, 39)} ...',
textDirection:
TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.black),
),
])
: Image.asset(
Myasset(img_num),
fit: BoxFit.cover,
width: MediaQuery.of(context)
.size
.width *
0.01,
height: MediaQuery.of(context)
.size
.height *
0.01,
),
],
),
),
),
);
}),
)
]),
);
}
}
Widget _listView(text_value) {
return Expanded(
child: ListView.builder(
itemCount: text_value.length,
itemBuilder: (context, index) {
var textVal = text_value[index];
String description =
textVal['description'] ?? 'we don\'t have description......';
var id_num =
textVal['id_num'].toString() ?? 'we don\'t have id_num......';
var img_num = textVal['img_num'].toString() ??
'we don\'t have img_num........... ';
print('id_num is ....$id_num' ' img_num is.....$img_num');
return Card(
margin: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
color: Colors.blue[50],
child: Theme(
data:
Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: InkWell(
onTap: (() => pushNewScreen(
context,
screen: isNumeric(id_num) == false
? ShowItem(
name: textVal['name'],
description: textVal['description'],
)
: ShowImage(
title: id_num,
image: Myasset(img_num),
),
withNavBar: true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation:
PageTransitionAnimation.slideRight,
)),
child: ExpansionTile(
title: Text(
isNumeric(id_num) == false ? textVal['name'] : id_num,
textDirection: TextDirection.rtl,
style:
const TextStyle(fontSize: 20.0, color: Colors.black54),
),
childrenPadding: const EdgeInsets.only(
bottom: 20.0, right: 20.0, left: 20.0, top: 5.0),
children: [
isNumeric(id_num) == false
? Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const Text(
'بیشتر',
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold),
),
Text(
'${description.substring(0, 39)} ...',
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(color: Colors.black),
),
])
: Image.asset(
Myasset(img_num),
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width * 0.01,
height: MediaQuery.of(context).size.height * 0.01,
),
],
),
),
),
);
}),
);
}
I didn't use getCatList & myImageListmethods because I encountered to error when I was using this methods.
How can I fix it?
Inside addListener you're calling loadData() every single time the user enter a character. Try this:
void initState() {
super.initState();
loadData();
txtQuery.addListener(() {
search(txtQuery.text);
});
}
The second thing you could try is use for in instead of forEach
void search(String query) {
if (query.isEmpty) {
textValues = [];
setState(() {});
return;
}
setState(() {
textValues = [
for (var item in yourDataSource)
if (item['key'].contains(query)) item
];
});
}
And you can use textValues in your list
body: ListView.builder(
itemCount: textValues.length,
You can use autocomplete textfield for your problem
check this plugin :
https://pub.dev/packages/auto_complete_search
You don't have any function for onChanged (TextFormField's argument), let's try this:
First edit your search() function
void search(String query) {
if (query.isEmpty) {
setState(() {
textValues = original;
});
return;
}
setState(() {
textValues = original.where( (element) =>
element['name'].contains(query.toLowerCase()).toString().toLowerCase() ||
element['description'].contains(query.toLowerCase()).toString().toLowerCase()
).toList();
});
}
Second edit onChanged argument
onChanged: (value) => search(value)
I am trying to build a questionnaire within an application
The idea is that the user answers questions
So every answer he gives is in one question
Accordingly he will receive the following question
For example if he answered a question with answer a
Will get one question
If he answered the same question with answer b
Will get another question later
how can i solve this
The code is attached
Thanks
import 'package:flutter/material.dart';
import 'package:gsheets/question_model.dart';
class QuizScreen extends StatefulWidget {
#override
State<QuizScreen> createState() => _QuizScreenState();
}
class _QuizScreenState extends State<QuizScreen> {
//define the datas
List<Question> questionList = getQuestions();
int currentQuestionIndex = 0;
int score = 0;
Answer? selectedAnswer;
int? nextQuestionId;
int? questionId;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 5, 50, 80),
body: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 32),
child:
Column(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
const Text(
"Simple Quiz App",
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
_questionWidget(),
_answerList(),
_nextButton(),
]),
),
);
}
_questionWidget() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Question ${currentQuestionIndex + 1}/${questionList.length.toString()}",
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 20),
Container(
alignment: Alignment.center,
width: double.infinity,
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(16),
),
child: Text(
questionList[currentQuestionIndex].questionText,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
)
],
);
}
_answerList() {
return Column(
children: questionList[currentQuestionIndex]
.answerList
.map(
(e) => _answerButton(e),
)
.toList(),
);
}
Widget _answerButton(Answer answer) {
bool isSelected = answer == selectedAnswer;
return Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 8),
height: 48,
child: ElevatedButton(
child: Text(answer.answerText),
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: isSelected ? Colors.orangeAccent : Colors.white,
onPrimary: isSelected ? Colors.white : Colors.black,
),
onPressed: () {
// if (selectedAnswer == null) {
// if (answer.isCorrect) {
// score++;
// }
setState(() {
selectedAnswer = answer;
});
}
// },
),
);
}
_nextButton() {
// if(answer == )
bool isLastQuestion = false;
if (currentQuestionIndex == questionList.length - 1) {
isLastQuestion = true;
}
return Container(
width: MediaQuery.of(context).size.width * 0.5,
height: 48,
child: ElevatedButton(
child: Text(isLastQuestion ? "Submit" : "Next"),
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: Colors.blueAccent,
onPrimary: Colors.white,
),
onPressed: () {
// if(nextQuestionId == questionId)
if (isLastQuestion) {
//display score
showDialog(context: context, builder: (_) => _showScoreDialog());
} else {
//next question
setState(() {
selectedAnswer = null;
currentQuestionIndex++;
});
}
},
),
);
}
_showScoreDialog() {
bool isPassed = false;
if (score >= questionList.length * 0.6) {
//pass if 60 %
isPassed = true;
}
String title = isPassed ? "Passed " : "Failed";
return AlertDialog(
title: Text(
title + " | Score is $score",
style: TextStyle(color: isPassed ? Colors.green : Colors.redAccent),
),
content: ElevatedButton(
child: const Text("Restart"),
onPressed: () {
Navigator.pop(context);
setState(() {
currentQuestionIndex = 0;
score = 0;
selectedAnswer = null;
});
},
),
);
}
}
========================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gsheets/quiz.dart';
class Question {
final String questionText;
final List<Answer> answerList;
final int questionId;
Question(this.questionText, this.answerList, this.questionId);
}
class Answer {
final String answerText;
final int nextQuestionId;
// final bool selectedOption;
Answer(this.answerText, this.nextQuestionId);
}
List<Question> getQuestions() {
List<Question> list = [];
list.add(Question(
"Which school did you learn",
[
Answer("a", 3),
Answer("b", 2),
],
3,
));
list.add(Question(
"what is your age",
[
Answer("18", 3),
Answer("20", 5),
],
2,
));
list.add(Question(
"which car do you drive",
[
Answer("c", 3),
Answer("d", 2),
],
3,
));
return list;
}
Keyboard triggers the api calling while closing and opening
I have search bar I didn't pass anything inside the search bar but when I click the search bar The api call will run.
I don't know why that thing was happen I tried several ways but It won't work
Even Inside the keyboard I choose the one hand keyboard that time also api will run
Also tried the Focusnode also.
class PopupNaviagator extends StatefulWidget {
var body;
String leadsName = "";
Map<String, dynamic> listValues;
PopupNaviagator(this.listValues);
var data;
String pageData = "";
double totalPageCount = 0;
int pageIncrementer = 1;
#override
_PopupNavigatorState createState() => _PopupNavigatorState();
}
class _PopupNavigatorState extends State<PopupNaviagator> {
Utils util = new Utils();
bool isSearching = false;
#override
void initState() {
super.initState();
}
String searchingText = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
),
title: !isSearching
? Text("Select Search Type")
: TextField(
autofocus: true,
autocorrect: true,
onChanged: (value) {
searchingText = value;
},
cursorColor: Colors.white,
onSubmitted: (value) {
searchingText = value;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
icon: Icon(Icons.search),
hintText: "Search ",
hintStyle: TextStyle(color: Colors.white)),
),
actions: <Widget>[
isSearching
? IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
searchingText = "";
this.isSearching = false;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
/* filteredCountries = countries;*/
});
},
)
: IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
this.isSearching = true;
});
},
)
],
),
body: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Scaffold(
body: FutureBuilder(
future: campaignAPI(widget.listValues["colname"].toString(), "1",
searchingText, 0),
builder: (context, snapshot) {
if (snapshot.hasData) {
var body;
if (widget.data == null) {
body = snapshot.data;
} else {
body = widget.data;
}
final records = body["list_records"];
widget.pageData = body["PageValues"];
widget.totalPageCount =
(body["TotalRecordCount"] / 5).toDouble();
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (records.length != 0) ...[
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(
top: 0,
bottom: 0,
),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
Map<String, dynamic> pickerValues =
records[index];
String titleName =
pickerValues["viewcol_value"]
.toString();
return Container(
margin: const EdgeInsets.all(5.00),
decoration: new BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey, width: 1.0),
borderRadius: new BorderRadius.all(
Radius.circular(3.0)),
),
child: FlatButton(
padding: EdgeInsets.all(8.0),
onPressed: () {
setState(() {
List<String> strArr = [
titleName,
pickerValues["rec_id"].toString(),
widget.listValues["colname"]
];
Navigator.pop(context, strArr);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(titleName.toString()),
Icon(
Icons.check_circle_outlined,
color: Colors.grey,
),
]),
),
);
}),
),
] else ...[
Container(
height: MediaQuery.of(context).size.height / 1.4,
child: Center(
child: Container(
padding: EdgeInsets.all(65),
child: Text(
"No Record Found",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black),
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
)),
),
),
],
if (records.length != 0) ...[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
if (widget.pageIncrementer > 1) {
widget.pageIncrementer--;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
setState(() {});
} else {
util.showToast("No records to show");
}
},
child: Icon(Icons.arrow_left_rounded,
size: 50, color: Colors.white),
),
Text(
widget.pageData,
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
InkWell(
onTap: () {
if (widget.totalPageCount > 1 &&
widget.totalPageCount >=
widget.pageIncrementer) {
widget.pageIncrementer++;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
} else {
util.showToast("No reocrds to show");
}
},
child: Icon(Icons.arrow_right_rounded,
size: 50, color: Colors.white),
),
],
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
)
]
],
),
),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error.toString()}');
}
return Center(
child: CircularProgressIndicator(),
);
},
),
),
));
}
campaignAPI(
String field_col, String page_no, String searchValue, int flag) async {
Utils util = new Utils();
SharedPreferences prefs = await SharedPreferences.getInstance();
final accessKey = prefs.getString('access_key');
final companyName = prefs.getString('company_name');
Object requestParam = {
"companyname": companyName,
"access_key": accessKey.toString(),
"field_col": field_col,
"page_no": page_no,
"searchValue": searchValue,
};
print(requestParam.toString());
APIS apis = new APIS();
Uri url = Uri.parse(apis.searchquickcreate);
util.prints(url);
util.prints(requestParam);
final response = await http.post(url, body: requestParam);
if (response.statusCode == 200) {
final body = json.decode(response.body);
print(body);
String status = body["status"];
if (status != "failed") {
if (flag == 1) {
setState(() {
widget.data = body;
});
}
return body;
} else {
util.logOut(context);
return body;
}
} else {
Navigator.pop(context);
util.showToast("Server Error !");
throw Exception("Server Error !");
}
}
}
i want to change the indexvalue (pictogramindex) of one page when we click nextbutton on another screen.I will explain briefly , I have 2 screens in my scenario the first screen contains an image and it's name , a textfield and nextbutton (i have provided a dummy data contains a list of image and it's names) the logic behind this is , when we complete the textfield box and click next button(after validate) the textfield value checks with the correctvalue which i was given in the dummy data and show it's synonym which also provided. when we click the next button we will go to another page which contains the correct answer(passed from first page) and a textfield in this the user can write about the correct answer ( validated) when click next button in this page (till this my applicationworks perfectly) i want to load the first page with it's index updated (+1) which i initialised as 0 (var pictogramindex=0). But in my case when coming back to first page the index is not updating it will automatically stores the initialised value. what i want is i want to update index on the first page when i click next button in the Second page .
my source code of first screen is shown here
class Pictogramscreen extends StatefulWidget {
final int length;
const Pictogramscreen({Key key, this.length}) : super(key: key);
#override
_PictogramscreenState createState() => _PictogramscreenState();
}
class _PictogramscreenState extends State<Pictogramscreen> {
#override
final _Key = GlobalKey<FormState>();
Color defaultcolor = Colors.blue[50];
Color trueColor = Colors.green;
Color falseColor = Colors.red;
Widget defcorrect = Text('');
var pictogramindex = 0;
TextEditingController usertitleInput = TextEditingController();
nextPictogram() {
setState(() {
pictogramindex++;
});
}
fillColor() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defaultcolor = trueColor
: defaultcolor = falseColor;
});
}
correctText() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defcorrect = Text(pictdata[pictogramindex]['pictsynonym'])
: defcorrect = Text(pictdata[pictogramindex]['pictcorrectword']);
});
}
reset() {
setState(() {
defaultcolor = Colors.blue[50];
defcorrect = Text('');
usertitleInput.clear();
});
}
void description(BuildContext ctx) {
Navigator.of(context).pushNamed('/user-description', arguments: {
'id': pictdata[pictogramindex]['pictid'],
'word': pictdata[pictogramindex]['pictcorrectword']
});
}
Widget build(BuildContext context) {
int length = pictdata.length;
return Scaffold(
body: pictogramindex < pictdata.length
? ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20),
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Card(
margin: EdgeInsets.only(top: 20),
child: Image.network(
pictdata[pictogramindex]['pictimg']),
),
SizedBox(
height: 10,
),
Text(
pictdata[pictogramindex]['pictword'],
style: TextStyle(
fontSize: 25,
),
),
SizedBox(
height: 10,
),
//Card(
//color: Colors.blue,
// child: TextField(
// decoration: InputDecoration.collapsed(
// hintText: 'type here'),
//textAlign: TextAlign.center,
// onSubmitted: (value) {
// usertitleInput = value;
// print(usertitleInput);
// },
// ),
//),
Form(
key: _Key,
child: TextFormField(
controller: usertitleInput,
validator: (usertitleInput) {
if (usertitleInput.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
fillColor: defaultcolor,
),
onFieldSubmitted: (value) {
usertitleInput.text = value;
fillColor();
correctText();
print(usertitleInput.text);
}),
),
SizedBox(
height: 10,
),
defcorrect,
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Key.currentState.validate()) {
description(context);
// nextPictogram();
reset();
}
//
//if (_Key.currentState.validate() == correctText()) {
// nextPictogram;
// }
},
child: Text('Next'),
)
],
),
),
],
)
: Center(
child: Text('completed'),
));
}
}
my source code of the second screen is show here
class Userinputscreen extends StatefulWidget {
final String id;
final String word;
const Userinputscreen({Key key, this.id, this.word}) : super(key: key);
#override
_UserinputscreenState createState() => _UserinputscreenState();
}
class _UserinputscreenState extends State<Userinputscreen> {
final _Keey = GlobalKey<FormState>();
TextEditingController userdescription = TextEditingController();
var pictogramindex;
void nextpict(BuildContext context) {
Navigator.of(context).pushNamed('/main-screen');
}
// void nextpict(BuildContext context, int index) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (ctx) => Pictogramscreen(
// index: i = 0,
// )));
// }
#override
Widget build(BuildContext context) {
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final correctWord = routeArgs['word'];
return MaterialApp(
home: Scaffold(
body: ListView(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.only(top: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
correctWord,
style: TextStyle(fontSize: 26),
),
SizedBox(
height: 10,
),
Form(
key: _Keey,
child: TextFormField(
controller: userdescription,
validator: (userdescription) {
if (userdescription.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
),
onFieldSubmitted: (value) {
userdescription.text = value;
print(userdescription.text);
}),
),
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Keey.currentState.validate()) {
nextpict(context);
}
},
child: Text('Next'),
)
],
),
),
),
),
])),
);
}
}
If I get it right, you basically want to tell the initial page that it's state is updated(the index) elsewhere. You basically need to make your app "reactive".
As is said in Google Developers Tutorial:
One of the advantages of Flutter is that it uses reactive views, which you can take to the next level by also applying reactive principles to your app’s data model.
Use some sort of state management. You need to choose from and use either Bloc, InheritedWidget and InheritedModel, Provider(ScopedModel), or the like.
Check this article on flutter about state management, or this for a complete list of approaches
I want to pass the String value of the selected Drop down currency item to the
fetchPost() Future function using the initialized String selectedSymbol but returns null Point Value.I tried to pass the SelectedCurrency instance to the selectedSymbol but it shows nullPointException. Here is the code:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(
MaterialApp(
title: 'Birr Converter App',
home: MainClass(),
),
);
class User {
User(this.name);
final String name;
}
class CurrencyClass {
final String currencyType;
CurrencyClass(this.currencyType);
}
class MainClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ConverterParent(),
);
// TODO: implement build
}
}
class ConverterParent extends StatefulWidget {
#override
_ConversionHouse createState() => _ConversionHouse();
}
class _ConversionHouse extends State<ConverterParent> {
User selectedLanguage;
String selectedSymbol ;
CurrencyClass selectedCurrency;
double exchangeData = 0.0;
Text birrName = Text('Birr',style: TextStyle(fontSize: 30.0),);
Text convertedAmount = Text('0.00');
Text convertText = Text('Convert', style: TextStyle(color: Colors.white,
fontSize: 25.0),);
final myController = TextEditingController();
List<User> users = <User>[
User('English'),
User('Amharic'),
User('Oromigna'),
User('Tigrigna'),
User('Somali')
];
List<CurrencyClass> currency = <CurrencyClass>[
CurrencyClass('USD'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
#override
void initState() {
super.initState();
selectedLanguage = users[0];
this.fetchPost();
}
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Birr Converter'),
backgroundColor: Colors.white10,
actions: <Widget>[
DropdownButton<User>(
value: selectedLanguage,
onChanged: (User newValue) {
setState(() {
selectedLanguage = newValue;
if (newValue == users[0]) {
currency = <CurrencyClass>[
CurrencyClass('ENG'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
birrName = Text(
'Birr',
style: TextStyle(fontSize: 30.0),
);
convertText = Text(
'Convert',
style: TextStyle(fontSize: 30.0, color: Colors.white),
);
} else if (newValue == users[1]) {
currency = <CurrencyClass>[
CurrencyClass('AMH'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
birrName = Text(
'Sop',
style: TextStyle(fontSize: 30.0),
);
convertText = Text(
'Lewet',
style: TextStyle(fontSize: 30.0, color: Colors.white),
);
} else if (newValue == users[2]) {
currency = <CurrencyClass>[
CurrencyClass('ORO'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
birrName = Text(
'Dim',
style: TextStyle(fontSize: 30.0),
);
convertText = Text(
'Jafida',
style: TextStyle(fontSize: 30.0, color: Colors.white),
);
} else if (newValue == users[3]) {
currency = <CurrencyClass>[
CurrencyClass('TIG'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
birrName = Text(
'Jaa',
style: TextStyle(fontSize: 30.0),
);
convertText = Text(
'Lewti',
style: TextStyle(fontSize: 30.0, color: Colors.white),
);
} else {
currency = <CurrencyClass>[
CurrencyClass('SOM'),
CurrencyClass('GBP'),
CurrencyClass('CAD'),
CurrencyClass('EUR'),
CurrencyClass('AUD'),
CurrencyClass('SAD'),
CurrencyClass('KAD'),
];
birrName = Text(
'Qoo',
style: TextStyle(fontSize: 30.0),
);
convertText = Text(
'Fola',
style: TextStyle(fontSize: 30.0, color: Colors.white),
);
}
selectedCurrency = currency[0];
});
},
items: users.map((User user) {
return DropdownMenuItem<User>(
value: user,
child: Text(
user.name,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
),
],
),
body: exchangeRate(),
);
}
Widget exchangeRate() {
return Container(
child:Padding(
padding: EdgeInsets.symmetric(vertical: 100.0,horizontal: 10.0),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 100.0),
child: Row(
children: <Widget>[
Container(
child: Expanded(
child: Center(
child: TextField(
decoration: InputDecoration.collapsed(
hintText: 'Enter..',
border: InputBorder.none,
),
keyboardType: TextInputType.number,
style: TextStyle(fontSize: 30.0,color: Colors.black),
controller: myController,
),
),
),
),
Padding(padding: EdgeInsets.symmetric(horizontal: 20.0),),
// birrDefinition,
DropdownButton<CurrencyClass>(
value: selectedCurrency,
onChanged: (CurrencyClass newExchange) {
setState(() {
selectedCurrency = newExchange;
selectedSymbol = selectedCurrency.toString();
});
},
items: currency.map((CurrencyClass currencyName) {
return DropdownMenuItem<CurrencyClass>(
value: currencyName,
child: Text(currencyName.currencyType,
style: TextStyle(color: Colors.red),),
);
}).toList()),
],
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 40.0),
),
Expanded(
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 50.0),
),
convertedAmount,//Converted Amount
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
),
birrName
],
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
),
Container(
height: 50.0,
width: 120.0,
child: FlatButton(
child: Center( child: convertText ),
onPressed: () {
setState(() {
convertedAmount = Text("${int.parse(myController.text) *
exchangeData}");
});
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.black87,
),
),
],
),
),
);
}
Future<double> fetchPost() async {
final response = await
http.get('https://openexchangerates.org/api/latest.json?
app_id=ffffffffffffsssssss');
if(response.statusCode == 200) {
final responseJson = json.decode(response.body);
exchangeData = responseJson["rates"][selectedSymbol];
return exchangeData;
}
else {
throw Exception('Failed to load post');
}
}
}
Instead of selectedCurrency you should use newExchange inside your onChanged
onChanged: (CurrencyClass newExchange) {
setState(() {
selectedCurrency = newExchange;
selectedSymbol = newExchange.currencyType;
});
}
After editing my code by #Dhiraj code,it didn't work but it helps.I removed fetchPost() function from initState and add it to the onChanged sectionlike this.
#override
void initState() {
super.initState();
selectedLanguage = users[0];
// this.fetchPost();
}
DropdownButton<CurrencyClass>(
value: selectedCurrency,
onChanged: (CurrencyClass newExchange) {
setState(() {
selectedCurrency = newExchange;
selectedSymbol = newExchange.currencyType ;
fetchPost();
});
},
items: currency.map((CurrencyClass currencyName) {
return DropdownMenuItem<CurrencyClass>(
value: currencyName,
child: Text(currencyName.currencyType,
style: TextStyle(color: Colors.red),),
);
}).toList()),