Related
I have ListView.seperated and a TextFormField in a Stack, how can I make those widgets not overlap?
Stack(
children: [
Builder(builder: (context) {
if (messages == null) {
return const Center(child: CircularProgressIndicator());
}
return RefreshIndicator(
onRefresh: () async {
ref
.read(messagesProvider.notifier)
.clearMessagesForChat(chat['id']);
},
child: ListView.separated(
controller: chatScrollController,
itemCount: messages.length,
separatorBuilder: (context, index) {
return const Divider(
height: 1,
);
},
itemBuilder: (BuildContext context, int index) {
final message = messages[index];
return ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
leading: GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
builder: (context) {
return UserProfile(message['author']);
}),
child:
getProfilePicture(context, message['author'], 30)),
title: Row(
children: [
Text(
message['author']['username'],
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
width: 16,
),
Text(
DateFormat.yMMMd().format(
DateTime.parse(message['created_at']).toLocal()),
style:
const TextStyle(color: Colors.grey, fontSize: 12),
)
],
),
subtitle: Text(
message['content'],
style: const TextStyle(fontSize: 16),
),
onTap: () {},
);
},
),
);
}),
Align(
alignment: Alignment.bottomLeft,
child: Container(
padding: const EdgeInsets.only(left: 10, bottom: 10, top: 10),
height: 60,
width: double.infinity,
color: Colors.white,
child: Row(
children: <Widget>[
const SizedBox(
width: 15,
),
Expanded(
child: TextField(
controller: messageController,
keyboardType: TextInputType.multiline,
minLines: 1,
maxLines: null,
onSubmitted: (_) => sendMessage(chat['id']),
decoration: const InputDecoration(
hintText: 'Write message...',
hintStyle: TextStyle(color: Colors.black54),
border: InputBorder.none),
),
),
const SizedBox(
width: 15,
),
FloatingActionButton(
onPressed: () => sendMessage(chat['id']),
backgroundColor: Colors.blue,
elevation: 0,
child: const Icon(
Icons.send,
color: Colors.white,
size: 18,
),
),
],
),
),
),
],
),
Thanks
I am working with Android Studio it runs really smooth. After some time of update. The app was really slow at getting data from the backend (testing backend with PostMan still fast) also execute other tasks. I wonder if our style of coding slow down the app. Have you ever experienced this ? Does the bad coding style has a huge effect on Emulator performance. If yes, could you please suggest a good way of coding that allows the app in Android Studio run faster.
Thank you so much.
Edit: Here is my code
import 'dart:convert';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/cupertino.dart' hide Image;
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/widgets.dart';
import 'package:goweefrontend/SizeConfig.dart';
import 'package:goweefrontend/View/Detail/DetailJourney.dart';
import 'package:goweefrontend/View/Map/TestMap.dart';
import 'package:goweefrontend/Model/LoggedUser.dart';
import 'package:goweefrontend/View/CustomBottomNavBar/CustomBottomNavBar.dart';
import 'package:goweefrontend/View/UserPage/CustomCalendar.dart';
import 'package:goweefrontend/View/UserPage/JoinRequest.dart';
import 'package:goweefrontend/View/UserPage/UserPageService.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:goweefrontend/View/LoginFunction/LoginComponent/LoginScreen.dart';
import '../../Constants.dart';
import 'package:http/http.dart' as http;
import '../../Model/Journey.dart';
class UserPage extends StatefulWidget {
static String routeName = "/userPage";
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
CalendarFormat format = CalendarFormat.twoWeeks;
DateTime focusedDay = DateTime.now();
DateTime selectedDay = DateTime.now();
bool upcomingIsVisible = false;
bool draftIsVisible = false;
bool passedIsVisible = false;
TextEditingController _lastNameController = TextEditingController();
TextEditingController _firstNameController = TextEditingController();
TextEditingController _ageController = TextEditingController();
TextEditingController _phoneNumberController = TextEditingController();
String _firstName;
String _lastName;
int _age;
String _phoneNumber;
String _imageURL;
List<Journey> journeyList = [];
Future _future;
Future loadUser() async {
String jsonString = await storage.read(key: "jwt");
final jsonResponse = json.decode(jsonString);
loggedUser = new LoggedUser.fromJson(jsonResponse);
getProfile();
}
//http APIs
editProfile(
String lastName, String firstName, String age, String phoneNumber) async {
await UserPageService().editProfile(lastName, firstName, age, phoneNumber);
}
getProfile() async {
var res = await http.get(
Uri.parse("$baseUrl/users/${loggedUser.user.userId}"),
headers: {
'Content_Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ${loggedUser.token}',
'Connection': 'Keep-Alive'
},
);
var data = jsonDecode(res.body);
loggedUser.user.firstName = data["firstName"];
loggedUser.user.lastName = data["lastName"];
loggedUser.user.age = data["age"];
loggedUser.user.phoneNumber = data["phoneNumber"];
loggedUser.user.userAvatar.imageLink = data["userAvatar"]["imageLink"];
setState(() {
_firstName = data["firstName"];
_lastName = data["lastName"];
_age = data["age"];
_phoneNumber = data["phoneNumber"];
_imageURL = data["userAvatar"]["imageLink"];
});
}
getJourneyByUserId() async {
setState(() {
journeyList = [];
});
var res = await http.get(
Uri.parse("$baseUrl/journeys/userid=${loggedUser.user.userId}"),
headers: {
'Content_Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ${loggedUser.token}',
'Connection': 'Keep-Alive'
},
);
var data = jsonDecode(res.body);
List idList = [];
for (var i in data) {
idList.add(i["journeyId"]);
}
for (var i in idList) {
var res = await http.get(
Uri.parse("$baseUrl/journeys/$i"),
);
var data = jsonDecode(res.body);
Journey userJourney = new Journey.fromJson(data);
setState(() {
journeyList.add(userJourney);
});
}
return journeyList;
}
logOut() async {
await UserPageService().logOut();
setState(() {
rangeList.clear();
journeyList.clear();
});
}
Future functionForBuilder() async {
return await getJourneyByUserId();
}
//function to print Journey Card
Widget printCard(Journey journey, IconData flexIcon, page, visible) {
return Visibility(
visible: visible,
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 5, 0, 5),
child: Container(
height: getHeight(0.1),
padding: EdgeInsets.fromLTRB(5, 10, 0, 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
offset: Offset(0, 8),
blurRadius: 5,
color: Colors.grey[300]),
]),
child: Row(
children: [
Padding(
//show journey image
padding: EdgeInsets.all(3),
child: CircleAvatar(
backgroundImage: NetworkImage(journey
.images[0].imageLink !=
null
? journey.images[0].imageLink
: "assets/images/Travel-WordPress-Themes.jpg.webp"),
radius: 20,
),
),
SizedBox(width: getWidth(0.02)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Container(
width: getWidth(0.32),
child: AutoSizeText(
"${journey.departure} - ${journey.destination}",
style: TextStyle(color: Colors.black),
overflow: TextOverflow.ellipsis,
),
),
],
),
],
),
SizedBox(width: getWidth(0.05)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Container(
width: getWidth(0.27),
child: AutoSizeText(
"${journey.startDate.day}/${journey.startDate.month} - "
"${journey.endDate.day}/${journey.endDate.month}/${journey.endDate.year}",
style:
TextStyle(color: Colors.grey, fontSize: 13),
maxLines: 1,
),
),
],
),
],
),
SizedBox(width: getWidth(0.05)),
Column(
children: [
Container(
width: getWidth(0.1),
child: TextButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => page));
},
child: FittedBox(
child: Icon(
flexIcon,
color: Colors.black,
),
),
),
)
],
)
],
),
),
)
],
),
);
}
//function to show dialog User Profile Card
showDialogFunc1(context) {
return showDialog(
context: context,
builder: (context) {
return Stack(alignment: Alignment.bottomCenter, children: [
Container(
margin: EdgeInsets.fromLTRB(10, 0, 10, 270),
height: getHeight(0.4),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(30.0)),
child: Padding(
padding: EdgeInsets.fromLTRB(20, 30, 0, 10),
child: Row(children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
printInfo("NAME", "$_lastName $_firstName"),
printInfo("AGE", "$_age"),
printInfo("PHONE", "$_phoneNumber"),
Center(
child: Container(
height: getHeight(0.05),
decoration: kBoxDecorationView,
child: TextButton(
onPressed: () {
showDialogFunc2(context);
},
child: Text(
"Edit Profile",
style: TextStyle(color: Colors.white),
),
),
),
)
]),
flex: 5,
),
Expanded(
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 40, 0),
child: Column(
children: [
CircleAvatar(
radius: 35,
backgroundImage: NetworkImage(
loggedUser.user.userAvatar.imageLink),
),
SizedBox(height: 15),
FittedBox(
child: Text(
"${loggedUser.user.email}",
style: TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.bold,
letterSpacing: 1.0,
decoration: TextDecoration.none),
),
)
],
),
)
],
),
flex: 5,
)
]))),
]);
});
}
//print User Information
printInfo(info1, info2) {
return FittedBox(
child: Row(
children: [
Text(
info1 + " : " + info2,
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
);
}
Widget loopPrintUpComeTrip() {
int counter = 0;
List<Widget> list = [];
for (var i in journeyList) {
if (i.status == "Upcoming") {
counter += 1;
if (counter == 1) {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), true));
} else {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), upcomingIsVisible));
}
}
}
if (counter == 0) {
list.add(Text("No Upcoming Trip to show"));
}
return Column(children: list);
}
Widget loopPrintInProgressTrip() {
int counter = 0;
List<Widget> list = [];
for (var i in journeyList) {
if (i.status == "In progress") {
counter += 1;
if (counter == 1) {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), true));
} else {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), draftIsVisible));
}
}
}
if (counter == 0) {
list.add(Text("No In Progress Trip to show"));
}
return Column(children: list);
}
Widget loopPrintFinishedTrip() {
int counter = 0;
List<Widget> list = [];
for (var i in journeyList) {
if (i.status == "Finished") {
counter += 1;
if (counter == 1) {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), true));
} else {
list.add(printCard(i, Icons.arrow_forward_ios,
DetailsJourneyScreen(journey: i), passedIsVisible));
}
}
}
if (counter == 0) {
list.add(Text(
"No Finished Trip to show",
style: TextStyle(color: Colors.grey),
));
}
return Column(children: list);
}
//print edit form
printFormField(info, controller) {
return Container(
height: getHeight(0.06),
child: TextButton(
onPressed: () {},
child: TextField(
controller: controller,
style: TextStyle(fontSize: 15),
decoration: InputDecoration(
hintText: "New $info",
contentPadding: EdgeInsets.fromLTRB(15, 5, 0, 5),
isDense: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
borderSide: BorderSide(color: kTextColor),
gapPadding: 0,
),
),
),
),
);
}
//show dialog edit profile
showDialogFunc2(context) {
return showDialog(
context: context,
builder: (context) {
return Stack(alignment: Alignment.bottomCenter, children: [
Container(
margin: EdgeInsets.fromLTRB(10, 0, 10, 270),
height: getHeight(0.4),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(30.0)),
child: Padding(
padding: EdgeInsets.fromLTRB(20, 30, 0, 10),
child: Row(children: [
Expanded(
child: Column(
//crossAxisAlignment: CrossAxisAlignment.start,
//mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
printFormField("Last Name", _lastNameController),
printFormField(
"First Name", _firstNameController),
printFormField("Age", _ageController),
printFormField(
"Phone Number", _phoneNumberController),
SizedBox(height: getHeight(0.01)),
Center(
child: Container(
height: getHeight(0.05),
decoration: kBoxDecorationView,
child: TextButton(
onPressed: () async {
await editProfile(
_lastNameController.text,
_firstNameController.text,
_ageController.text,
_phoneNumberController.text);
_lastNameController.clear();
_firstNameController.clear();
_ageController.clear();
_phoneNumberController.clear();
Navigator.pop(context);
Navigator.pop(context);
setState(() {
getProfile();
});
},
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
),
))
]),
flex: 6,
),
Expanded(
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 40, 0),
child: Column(
children: [
CircleAvatar(
radius: 35,
backgroundImage: NetworkImage(
loggedUser.user.userAvatar.imageLink),
),
SizedBox(height: 10),
Text(
"Upload Image",
style: TextStyle(
fontSize: 10,
color: Colors.black,
fontWeight: FontWeight.bold,
letterSpacing: 1.0,
decoration: TextDecoration.none),
)
],
),
)
],
),
flex: 4,
)
]))),
]);
});
}
#override
void initState() {
super.initState();
loadUser();
_future = getJourneyByUserId();
}
//screen
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: kColorPalette4,
appBar: AppBar(
title: Text(
"User Profile",
style: TextStyle(color: kColorPalette4),
),
centerTitle: true,
backgroundColor: kBackgroundColor,
elevation: 0.0,
titleSpacing: 1.0,
),
body: Container(
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
Text("Loading...")
]),
);
} else {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(10, 5, 10, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
//user avatar
child: TextButton(
onPressed: () {
showDialogFunc1(context);
getProfile();
},
child: CircleAvatar(
backgroundImage: NetworkImage(_imageURL != null
? _imageURL
: "https://www.pngitem.com/pimgs/m/256-2560208_person-icon-black-png-transparent-png.png"),
radius: 50,
),
)),
SizedBox(height: 5),
CustomCalendar(),
Text("Trip Requests",
style: TextStyle(
color: kBackgroundColor,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
fontSize: 15)),
SizedBox(height: 10),
JoinRequest(),
SizedBox(height: 10),
Row(
children: [
Container(
width: getWidth(0.35),
child: AutoSizeText(
"Upcoming Trips",
style: TextStyle(
color: kColorUpComing,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
fontSize: 15),
maxLines: 1,
),
),
Padding(
padding: EdgeInsets.fromLTRB(15, 0, 0, 0),
child: Container(
height: getHeight(0.05),
decoration: kBoxDecorationView,
child: TextButton(
child: Text(
"View All",
style: TextStyle(color: kColorPalette4),
),
onPressed: () {
setState(() {
upcomingIsVisible = !upcomingIsVisible;
});
},
),
),
)
],
),
//upcoming trip card
SizedBox(height: 5),
loopPrintUpComeTrip(),
//Calendar Field
SizedBox(height: 5),
Row(
children: [
Container(
width: getWidth(0.35),
child: AutoSizeText(
"Finished Trips",
style: TextStyle(
color: kColorFinished,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
fontSize: 15),
maxLines: 1,
),
),
Padding(
padding: EdgeInsets.fromLTRB(15, 0, 0, 0),
child: Container(
height: getHeight(0.05),
decoration: kBoxDecorationView,
child: TextButton(
child: Text(
"View All",
style: TextStyle(color: kColorPalette4),
),
onPressed: () {
setState(() {
passedIsVisible = !passedIsVisible;
});
},
),
),
)
],
),
SizedBox(height: 5),
loopPrintFinishedTrip(),
SizedBox(height: 5),
Row(
children: [
Container(
width: getWidth(0.35),
child: AutoSizeText(
"In Progress Trips",
style: TextStyle(
color: kColorInProgress,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
fontSize: 15),
maxLines: 1,
),
),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Container(
height: getHeight(0.05),
decoration: kBoxDecorationView,
child: TextButton(
child: Text(
"View All",
style: TextStyle(color: kColorPalette4),
),
onPressed: () => setState(
() => draftIsVisible = !draftIsVisible),
),
),
)
],
),
SizedBox(height: 5),
loopPrintInProgressTrip(),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: getHeight(0.055),
decoration: kBoxDecorationView,
alignment: Alignment.center,
child: TextButton(
onPressed: () async {
await logOut();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
LoginScreen()));
},
child: FittedBox(
child: Row(
children: [
Icon(
Icons.logout,
color: kColorPalette4,
size: 20,
),
AutoSizeText(
"Log Out",
style: TextStyle(
color: kColorPalette4,
),
),
],
),
)),
)
],
),
SizedBox(
height: getHeight(0.01),
)
],
),
),
);
}
},
),
));
}
}
Usually when i'm dealing with retrieving and sending data to the database I create a new thread to do the job of transferring data, if you are transferring with the main thread it's not recommended to do that the program could crash.
There is a way to see the performance of the app:
Profile your app performance
Another reason to be lagging could be:
Style of coding won't slow your program down. However, an inefficient
algorithm will. – Learning Mathematics 8 hours ago
another reason could be the version of the Android you are using to operate in the app some View object has specific limitations depending of the version of the Android.
This is set of expansion tile. Here I want to warp Expansion tile inside a container so that i can give border , shape and shadow to the tile. Also
I want the expanded result as same like this. How can I achieve this. Please help me
I have tried the below pattern. But when I expand I am getting Render overflow error
Container(
height: 80,
width: MediaQuery.of(context).size.width - 10,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Theme.of(context).hintColor.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 5)
],
),
child:
ExpansionTile(
backgroundColor: Colors.white,
trailing: Icon(Icons.arrow_forward_ios_rounded),
initiallyExpanded: false,
title: Text(
'Messages',
style: Theme.of(context)
.textTheme
.subtitle),
children: List.generate(
3, (indexProduct) {
return Text("terwyteuwte");
}),
)
),
please help me..
You can do the following thing
Add the following widgets in ListView() according to your use
class ItemTile extends StatefulWidget {
// final OrderItem orderItem;
// OrderItemTile(this.title);
#override
_ItemTileState createState() => _ItemTileState();
}
class _ItemTileState extends State<ItemTile> {
bool _expanded = false;
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
height: _expanded
? 350
: 100,
child: Card(
elevation: 10,
color: Theme.of(context).canvasColor,
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
children: <Widget>[
ListTile(
title: (Text(
'File',
style: TextStyle(
fontSize: 22, fontWeight: FontWeight.bold),
)),
trailing: IconButton(
icon: _expanded
? Icon(Icons.expand_less)
: Icon(Icons.expand_more),
onPressed: () {
setState(() {
_expanded = !_expanded;
});
}),
),
AnimatedContainer(
duration: Duration(milliseconds: 300),
height: _expanded
? 300
: 0,
width: MediaQuery.of(context).size.width,
child: ItemExpandedTile(),
)
],
),
),
);
}
}
the widget which is shown after expanding
class ItemExpandedTile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Stack(
children: <Widget>[
Positioned(
top: 5,
child: Container(
height: 90,
width: MediaQuery.of(context).size.width - 75,
padding: EdgeInsets.all(10),
decoration: new BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 15.0,
spreadRadius: 0.5,
offset: Offset(
1.0,
1.0,
),
)
],
),
child: Row(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(
'Title',
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.bold),
),
],
),
],
),
),
),
],
),
);
}
}
Th Result:
I'm fairly new to software dev using Flutter/Dart and I'm currently working on a widget that uses listview to display the information entered by a user. This information is currently stored in a List and for some reason, each time I add new info to the list, only the info at the first index is printed. I'm not actually getting any error message so it's pretty hard to trace it. I've tried debugging it using print statements but it's really not helping me.
The second problem is that when the user chooses a picture from their gallery, it isn't immediately displayed.
How can I fix these things?
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:mind_matters/shared/loading.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
class EmergencyContacts extends StatefulWidget {
#override
_EmergencyContactsState createState() => _EmergencyContactsState();
}
class _EmergencyContactsState extends State<EmergencyContacts> {
bool loading = false;
static bool buttonPressed = true;
static String emergencyName = "";
static String emailAddress = "";
static String phoneNumber = "";
static File _contactPic;
static int index = -1;
Future getPic() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_contactPic = image;
print('Image path is: $_contactPic');
});
}
Future uploadPic(BuildContext context) async {
String fileName = basename(_contactPic.path);
StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child(fileName);
StorageUploadTask uploadTask = firebaseStorageRef.putFile(_contactPic);
StorageTaskSnapshot takeSnapshot = await uploadTask.onComplete;
}
List<EmergencyContact> emergencyContacts = [];
void createInstanceOfContact() {
if (emergencyName.isNotEmpty) {
emergencyContacts.add(EmergencyContact(
contactPic: _contactPic,
emergencyName: emergencyName,
phoneNumber: phoneNumber,
emailAddress: emailAddress));
} else if (emergencyName.isEmpty) {
emergencyContacts.add(EmergencyContact(
contactPic: null,
emergencyName: "null",
phoneNumber: "null",
emailAddress: "null"));
}
}
void incrementIndex() {
if (buttonPressed){
index++;
}
}
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xffff696A),
elevation: 0,
leading: new IconButton(
icon: new Icon(Icons.arrow_back, color: Colors.black45),
onPressed: () => Navigator.of(context).pop(),
),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: <Widget>[
Text(" Add emergency contacts",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Poppins',
color: Colors.black54,
fontSize: 20,
fontWeight: FontWeight.bold,
)),
],
),
],
),
automaticallyImplyLeading: false,
centerTitle: true,
),
body: Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: [const Color(0xffff696A), const Color(0xffca436b)],
begin: FractionalOffset.topLeft,
end: FractionalOffset.bottomRight,
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: SingleChildScrollView(
child: Container(
height: 100000,
child: Padding(
padding: EdgeInsets.fromLTRB(20, 0, 20, 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(height: 20),
Container(
child: Padding(
padding: EdgeInsets.all(10),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"For your safety, please enter at least 1 phone number "
"and email. "
"It will be used to contact someone if we or "
"someone you're talking to feel you need "
"immediate help.",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 16,
)),
SizedBox(height: 20),
new ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) =>
new CardRow(
emergencyContacts[index]),
itemCount: emergencyContacts.length,
),
Text("the index here is $index"),
SizedBox(height: 10),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: 194,
child: FloatingActionButton(
onPressed: () {
setState(() {
_contactPic = null;
emergencyName = "";
emailAddress = "";
phoneNumber = "";
});
showAlertDialog(context);
},
child: Icon(
Icons.add,
),
foregroundColor: Colors.white,
backgroundColor:
const Color(0xffff696A),
mini: true,
elevation: 10,
),
),
),
SizedBox(height: 10),
Container(
width: 106,
child: RaisedButton(
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(
18.0),
side: BorderSide(
color: const Color(0xffff696A),
)),
elevation: 10,
//:todo don't forget on pressed action
onPressed: () async {
},
color: const Color(0xffff696A),
textColor: Colors.white,
child: Row(children: <Widget>[
Text("next step".toUpperCase(),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14)),
]),
),
),
],
),
),
),
),
])),
),
),
));
}
showAlertDialog(BuildContext context) {
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: Container(
height: 422,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Add Contact",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Poppins',
color: const Color(0xffff696A),
fontSize: 20,
fontWeight: FontWeight.bold,
)),
SizedBox(height: 10),
CircleAvatar(
radius: 50,
backgroundColor: const Color(0xffff696A),
child: ClipOval(
child: SizedBox(
width: 92,
height: 92,
child: (_contactPic != null)
? Image.file(_contactPic, fit: BoxFit.fill)
: Image.network(
'IMAGE',
fit: BoxFit.fill,
)),
),
),
Padding(
padding: EdgeInsets.only(left: 26),
child: IconButton(
icon: Icon(
Icons.photo_camera,
size: 30,
),
onPressed: () {
getPic();
},
),
),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => emergencyName = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person,
),
hintText: "Enter their name/nickname",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
SizedBox(height: 10),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => emailAddress = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.email,
),
hintText: "Enter their email address",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
SizedBox(height: 10),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => phoneNumber = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone,
),
hintText: "Enter their phone number",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
Align(
alignment: Alignment.center,
child: Row(
children: <Widget>[
FlatButton(
child: Text("Cancel",
style: TextStyle(
color: const Color(0xffff696A),
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
onPressed: () {
Navigator.of(context).pop();
},
),
SizedBox(width: 80),
FlatButton(
child: Text("Add Contact",
style: TextStyle(
color: const Color(0xffff696A),
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
onPressed: () async {
setState(() {
buttonPressed = true;
});
if (buttonPressed){
createInstanceOfContact();
incrementIndex();
}
print("the index is $index");
print(emergencyContacts[index].contactPic.toString());
print(emergencyContacts[index].emergencyName);
print(emergencyContacts[index].phoneNumber);
print(emergencyContacts[index].emailAddress);
Navigator.of(context).pop();
print(_contactPic.toString());
print(emergencyName);
print(emailAddress);
print(phoneNumber);
},
),
],
),
)
],
),
),
),
),
);
});
}
}
class CardRow extends StatelessWidget {
static EmergencyContact emergencyContact;
static File emergencyContactPic = _EmergencyContactsState._contactPic;
static String emergencyName = _EmergencyContactsState.emergencyName;
static String emergencyNumber = _EmergencyContactsState.phoneNumber;
static String emergencyEmail = _EmergencyContactsState.emailAddress;
CardRow(emergencyContact);
#override
Widget build(BuildContext context) {
return new Container(
height: 130,
margin: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
child: new Stack(
children: <Widget>[
contactCard,
contactCardContent,
contactThumbnail,
deleteContact,
],
));
}
final contactThumbnail = new Container(
//margin: new EdgeInsets.only(top: 3, left: 270),
margin: new EdgeInsets.only(bottom: 5),
alignment: FractionalOffset.centerLeft,
child: CircleAvatar(
radius: 46,
backgroundColor: new Color(0xFF733b67),
child: ClipOval(
child: SizedBox(
width: 82,
height: 82,
child: (emergencyContactPic != null)
? Image.file(emergencyContactPic, fit: BoxFit.fill)
: Image.network(
'IMAGE',
fit: BoxFit.fill,
)),
)),
);
final contactCard = new Container(
height: 124,
margin: new EdgeInsets.only(left: 46),
decoration: new BoxDecoration(
color: new Color(0xFF733b67),
shape: BoxShape.rectangle,
borderRadius: new BorderRadius.circular(8),
boxShadow: <BoxShadow>[
new BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: new Offset(0, 10),
)
]),
);
final deleteContact = new Container(
margin: new EdgeInsets.only(top: 3, left: 270),
child: IconButton(
icon: new Icon (Icons.delete_sweep,
size: 30,
),
color: const Color(0xffff696A),
onPressed: (){
//todo delete card
},
),
);
final contactCardContent = new Container(
margin: new EdgeInsets.fromLTRB(90, 16, 16, 16),
constraints: new BoxConstraints.expand(),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(height: 4.0),
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(
Icons.person,
size: 20,
),
new SizedBox(width: 10.0),
new Text(emergencyName,
style: TextStyle(
color: Colors.white,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
]),
new SizedBox(height: 10.0),
Row(children: <Widget>[
Icon(
Icons.phone,
size: 20,
),
new SizedBox(width: 10.0),
Text(emergencyNumber,
style: TextStyle(
color: Colors.grey,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
]),
new Container(
margin: new EdgeInsets.symmetric(vertical: 8.0),
height: 2.0,
width: 18.0,
color: const Color(0xffff696A)),
new Row(
children: <Widget>[
Icon(
Icons.mail,
size: 20,
),
new SizedBox(width: 10.0),
new Text(emergencyEmail,
style: TextStyle(
color: Colors.grey,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
],
),
],
),
);
}
class EmergencyContact {
File contactPic;
String emergencyName;
String phoneNumber;
String emailAddress;
EmergencyContact(
{this.contactPic,
this.emergencyName,
this.phoneNumber,
this.emailAddress});
}
The reason the listView.builder is not functioning correctly is that you have declared the variables in the CardRow Widget and are using those static values instead of the arguments that you have passed. You are passing the arguments, you just aren't using them.
The only change I did here:
final EmergencyContact emergencyContact;
//static File emergencyContactPic = _EmergencyContactsState._contactPic;
// static String emergencyName = _EmergencyContactsState.emergencyName;
// static String emergencyNumber = _EmergencyContactsState.phoneNumber;
// static String emergencyEmail = _EmergencyContactsState.emailAddress;
CardRow(this.emergencyContact);
Here is where I extracted your widgets as methods(just the ones passed in the argument), the reason I did this was that this can't be accessed in the initializers that you had done. I would recommend you to start using final more, and extracting your Widgets into their own Stateful/Stateless Widgets, or as methods, as I have done below. It's good practice.
child: new Stack(
children: <Widget>[
contactCard,
contactCardContent(),
contactThumbnail(),
deleteContact,
],
));
Container contactThumbnail() {
return Container(
//margin: new EdgeInsets.only(top: 3, left: 270),
margin: new EdgeInsets.only(bottom: 5),
alignment: FractionalOffset.centerLeft,
child: CircleAvatar(
radius: 46,
backgroundColor: new Color(0xFF733b67),
child: ClipOval(
child: SizedBox(
width: 82,
height: 82,
child: (emergencyContact.contactPic != null)
? Image.file(emergencyContact.contactPic, fit: BoxFit.fill)
: Image.network(
'IMAGE',
fit: BoxFit.fill,
)),
)),
);
}
Container contactCardContent() {
return new Container(
margin: new EdgeInsets.fromLTRB(90, 16, 16, 16),
constraints: new BoxConstraints.expand(),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(height: 4.0),
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(
Icons.person,
size: 20,
),
new SizedBox(width: 10.0),
new Text(emergencyContact.emergencyName,
style: TextStyle(
color: Colors.white,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
]),
new SizedBox(height: 10.0),
Row(children: <Widget>[
Icon(
Icons.phone,
size: 20,
),
new SizedBox(width: 10.0),
Text(emergencyContact.phoneNumber,
style: TextStyle(
color: Colors.grey,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
]),
new Container(
margin: new EdgeInsets.symmetric(vertical: 8.0),
height: 2.0,
width: 18.0,
color: const Color(0xffff696A)),
new Row(
children: <Widget>[
Icon(
Icons.mail,
size: 20,
),
new SizedBox(width: 10.0),
new Text(emergencyContact.emailAddress,
style: TextStyle(
color: Colors.grey,
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
],
),
],
),
);
}
Second Problem
For your second problem, the reason you don't see the image being displayed immediately is that the showDialog is not being rebuilt. To explicate more on this, the setState that is being used in the showAlertDialog function belongs to the parent Widget, so, that means that it is only going to be rebuilding what is outside of that particular function. The showAlertDialog function only gets rebuilt when you first click on it. In order to fix this, we will either have to put the showDialog in a Stateful widget or use a StatefulBuilder (https://api.flutter.dev/flutter/widgets/StatefulBuilder-class.html)
The StatefulBuilder comes with its own setState construction, meaning it can now get rebuilt when using setState, resulting in displaying the image immediately. BUT now we have another issue, I see you have the boolean buttonPressed in there, that whole setState(boolean) belongs to the parent widget, not the StatefulBuilder setState, so now this means that the list will not be displayed after "add contacts" is pressed until it gets rebuilt. We need the setState from the parent, not from the StatefulBuilder. There are many ways to fix this, but I personally think using a callback is one of the best. First, you will need to create a function, like rebuildWidget and simply change the state right there. Secondly, pass that function down to showAlertDialog as an argument. Thirdly, go ahead and create a final Function and then assign it. Last, simply call it on the onPressed.
void rebuildWidget() {
setState(() {
buttonPressed = true;
});
}
showAlertDialog(
context,
rebuildWidget
);
showAlertDialog(BuildContext context, myState) {
final Function() updateParent = myState;
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
File myPic;
return StatefulBuilder(
builder: (context, setState) {
return Dialog(
// title: Text("Title of Dialog"),
child: Container(
height: 422,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Add Contact",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Poppins',
color: const Color(0xffff696A),
fontSize: 20,
fontWeight: FontWeight.bold,
)),
SizedBox(height: 10),
CircleAvatar(
radius: 50,
backgroundColor: const Color(0xffff696A),
child: ClipOval(
child: SizedBox(
width: 92,
height: 92,
child: myPic != null
? Image.file(myPic, fit: BoxFit.fill)
: Image.network(
'IMAGE',
fit: BoxFit.fill,
)),
),
),
Padding(
padding: EdgeInsets.only(left: 26),
child: IconButton(
icon: Icon(
Icons.photo_camera,
size: 30,
),
onPressed: () async {
await getPic();
setState(() { // To rebuild this method
myPic = _contactPic;
});
},
),
),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => emergencyName = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person,
),
hintText: "Enter their name/nickname",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
SizedBox(height: 10),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => emailAddress = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.email,
),
hintText: "Enter their email address",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
SizedBox(height: 10),
Container(
width: 320,
child: Form(
//key: _formKey2,
child: TextFormField(
obscureText: false,
onChanged: (val) {
setState(() => phoneNumber = val);
},
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone,
),
hintText: "Enter their phone number",
hintStyle: TextStyle(
fontFamily: "Poppins",
color: Colors.black,
height: 1.2,
fontSize: 14,
)),
),
),
),
Align(
alignment: Alignment.center,
child: Row(
children: <Widget>[
FlatButton(
child: Text("Cancel",
style: TextStyle(
color: const Color(0xffff696A),
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
onPressed: () {
Navigator.of(context).pop();
},
),
SizedBox(width: 80),
FlatButton(
child: Text("Add Contact",
style: TextStyle(
color: const Color(0xffff696A),
fontFamily: "Poppins",
height: 1.2,
fontSize: 14,
)),
onPressed: () async {
updateParent();
if (buttonPressed) {
createInstanceOfContact();
incrementIndex();
}
print("the index is $index");
print(emergencyContacts[index]
.contactPic
.toString());
print(emergencyContacts[index].emergencyName);
print(emergencyContacts[index].phoneNumber);
print(emergencyContacts[index].emailAddress);
Navigator.of(context).pop();
print(_contactPic.toString());
print(emergencyName);
print(emailAddress);
print(phoneNumber);
},
),
],
),
)
],
),
),
),
),
);
},
);
},
);
}
}
Am writing a very basic app that just displays numbers on the screen and you can increase and decrease the value of this number with some buttons.
Works fine, except for some reason the text display often goes nuts. I'll show you what I mean:
The above text is trying to display a value of 20, but it is struggling for some reason, as you can see.
This only appears to be happening with double-digit or higher values. My code:
class _SinglePlayerModeParentState extends State<SinglePlayerMode> {
#override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIOverlays([]);
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft,]);
}
#override
Widget build(BuildContext context) {
Widget numberDisplay = GestureDetector(
onTap: () {
print("GestureDetector");
_incrementBy1();
},
child: Center(child: Text(_numberValue.toString(),
style: TextStyle(
fontSize: 200.0,
color: Colors.white,
),
),
),
);
This code was already displaying these text issues before I rearranged the code to include the following as well:
Widget buttonRow() {
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 40.0, right: 20.0),
child: FloatingActionButton(
heroTag: null,
onPressed: _numberValueMinus5,
child: Text('-5'),
),
),
Padding(
padding: EdgeInsets.only(left: 20.0, right: 40.0),
child: FloatingActionButton(
heroTag: null,
onPressed: _numberValueMinus1,
child: Text('-1'),
),
),
],
),
Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 40.0, right: 20.0),
child: FloatingActionButton(
heroTag: null,
onPressed: _numberValuePlus1,
child: Text('+1'),
),
),
Padding(
padding: EdgeInsets.only(left: 20.0, right: 40.0),
child: FloatingActionButton(
heroTag: null,
onPressed: _numberValuePlus5,
child: Text('+5'),
),
),
],
),
],
);
}
return MaterialApp(
title: 'Bean Counter',
home: Scaffold(
backgroundColor: Colors.blueAccent,
body: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 90.0, bottom: 40.0),
child: numberDisplay,
),
buttonRow(),
],
),
),
);
}
Not sure what I can do differently here, it's just text!
Any thoughts?
Edit: Testing the FittedBox solution.
#override
Widget build(BuildContext context) {
Widget numberDisplay = GestureDetector(
onTap: () {
print("GestureDetector");
_incrementBy1();
},
child: Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(_numberValue.toString(),
style: TextStyle(
fontSize: 200.0,
color: Colors.white,
),
),
),
),
);
}
Results: this is trying to display the number 30:
As you can see, it still isn't displaying properly. I also tried BoxFit.contain, same issue. Information obtained from this reference: https://docs.flutter.io/flutter/painting/BoxFit-class.html
Any other ideas guys? Or did I implement FittedBox incorrectly?
Edit: Tried with AutoSizeText, as recommended below. Code:
#override
Widget build(BuildContext context) {
Widget scoreText = GestureDetector(
onTap: () {
print("GestureDetector");
_incrementBy1();
},
child: Center(
child: AutoSizeText(_numberValue.toString(),
style: TextStyle(
fontSize: 200.0,
color: Colors.white,
),
),
),
);
Results:
Argh! Displaying text on a clear screen. Level: difficult.