I am trying to implement a review page for products.
I trying to show the products images in a expansionTile (see second image).
Under the expansionTile I add my buttons.
My problem:
To show the expansionTile list I must give the container a fix height.
But if I have less images in my list the screen show a white space (second image).
How can I make the container height dynamic to hide the white space?
Here my example:
If I add some pictures my screen looks like this.
How I can hide the white space between list and buttons?
Here my Code:
Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.8,
child: ListView(
children: [
ExpansionTile(
title: Text('Pictures'),
onExpansionChanged: (value) {
setState(
() {},
);
},
children: List<Widget>.generate(
_imageList.length,
(index) => ListTile(
title: Text(_imageList[0]
.path
.split('/')
.last
.length >
25
? _imageList[0]
.path
.split('/')
.last
.substring(0, 25) +
'...'
: _imageList[index].path.split('/').last),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: _selected![index]
? Colors.red
: Colors.red,
),
onPressed: () {
setState(() {
_selected![index] =
!_selected![index];
_imageList.removeAt(index);
});
},
),
],
),
),
),
),
],
),
)
Here my full code:
class ReviewPage1 extends StatefulWidget {
//passed paramter
final String _productID;
ReviewPage1(this._productID, {Key? key}) : super(key: key);
#override
_ReviewPage1 createState() => _ReviewPage1(_productID);
}
class _ReviewPage1 extends State<ReviewPage1> {
///passed paramter
final String _productID;
_ReviewPage1(this._productID);
//controller
final TextEditingController _controllerReviewTitle = TextEditingController();
final TextEditingController _controllerReviewDescription =
TextEditingController();
//image
List<File> _imageList = [];
var _image;
var imagePicker;
//list tile color
List<bool>? _selected = [];
//stars
var rating = 1;
#override
void initState() {
super.initState();
imagePicker = new ImagePicker();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: customSubAppBar("Create Review", context),
body: SingleChildScrollView(
child: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
children: [
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.05,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Rate your expreince',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.02,
MediaQuery.of(context).size.width * 0,
null,
),
//stars rating
Row(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(4, (index) {
return GestureDetector(
onTap: () {
setState(() {
// Toggle light when tapped.
print('star at index: ' + index.toString());
rating = index + 1;
});
},
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Icon(
//index < rating ? Icons.star : Icons.star_border,
Icons.star,
size: 30,
color: index < rating
? Theme.of(context).primaryColor
: Colors.black.withOpacity(0.5),
)));
}),
),
Spacer(),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Review Title',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//textfield review title
customDefaultTextField(
50,
1,
TextInputType.text,
_controllerReviewTitle,
1,
false,
Colors.black87,
Theme.of(context).primaryColor,
false,
Theme.of(context).primaryColor,
0,
0,
Theme.of(context).primaryColor,
0,
0,
'Title *',
'Enter Your Title',
Colors.black12,
2,
0,
Theme.of(context).primaryColor,
2,
0,
null,
0,
0,
0,
0,
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Write Review',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//textfield review description
customDefaultTextField(
50,
1,
TextInputType.text,
_controllerReviewDescription,
1,
false,
Colors.black87,
Theme.of(context).primaryColor,
false,
Theme.of(context).primaryColor,
0,
0,
Theme.of(context).primaryColor,
0,
0,
'Write your Expreinces *',
'Enter your Expreinces',
Colors.black12,
2,
0,
Theme.of(context).primaryColor,
2,
0,
null,
0,
0,
0,
0,
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Enter your picture',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.03,
MediaQuery.of(context).size.width * 0,
null,
),
//take image
GestureDetector(
onTap: () async {
var source = ImageSource.camera;
XFile? image = await imagePicker.pickImage(
source: source,
imageQuality: 50,
preferredCameraDevice: CameraDevice.front);
setState(
() {
_image = File(image!.path);
_imageList.add(_image);
_selected!.add(false);
},
);
},
child: Stack(
children: [
//icon
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
decoration: BoxDecoration(
color:
Theme.of(context).primaryColor.withOpacity(0.8),
borderRadius: BorderRadius.all(Radius.circular(8))),
child: Center(
child: customIcon(
Icons.camera_alt_rounded, Colors.black87, 30)),
),
//border
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
child: DashedRect(
color: Colors.black87,
strokeWidth: 2.0,
gap: 10.0,
),
),
],
),
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.03,
MediaQuery.of(context).size.width * 0,
null,
),
//show images
_imageList.length != 0
? //IntrinsicHeight(
Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.8,
child: ListView(
children: [
ExpansionTile(
title: Text('Pictures'),
onExpansionChanged: (value) {
setState(
() {},
);
},
children: List<Widget>.generate(
_imageList.length,
(index) => ListTile(
title: Text(_imageList[0]
.path
.split('/')
.last
.length >
25
? _imageList[0]
.path
.split('/')
.last
.substring(0, 25) +
'...'
: _imageList[index].path.split('/').last),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: _selected![index]
? Colors.red
: Colors.red,
),
onPressed: () {
setState(() {
_selected![index] =
!_selected![index];
_imageList.removeAt(index);
});
},
),
],
),
),
),
),
],
),
)
//)
: Container(),
//text cancel
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextButton(
child: Text(
'Send Review',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Colors.greenAccent[400],
onSurface: Colors.grey,
),
onPressed: () {
if (_checkInputValuesEmpty(
context, _controllerReviewTitle.text, 'title') &&
_checkInputValuesEmpty(
context,
_controllerReviewDescription.text,
'description')) {
final String _userID =
FirebaseAuth.instance.currentUser!.uid;
_uploadFile(_userID, _productID, context, _imageList);
}
},
),
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.01,
MediaQuery.of(context).size.width * 0,
null,
),
//text send review
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextButton(
child: Text(
'Cancel',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Color.fromRGBO(170, 170, 170, 1),
onSurface: Colors.grey,
),
onPressed: () async {},
),
),
],
),
),
),
),
//sidebar
drawer: CustomSideBar(context),
);
}
//upload review images
Future _uploadFile(_userID, _productID, context, _imageList) async {
var downloadURLs = [];
for (var image in _imageList) {
String url;
String postId = DateTime.now().millisecondsSinceEpoch.toString();
String fileName = 'reviewImage_${_productID}_${postId}';
Reference ref = FirebaseStorage.instance
.ref()
.child("images/review/review_images/" + _productID + '/' + _userID)
.child(fileName);
await ref.putFile(image);
url = fileName;
downloadURLs.add(url);
}
//upload review data
_addReviewData(_productID, downloadURLs, _userID);
}
//add review data in db
_addReviewData(_productID, downloadURLs, _userID) async {
final String _userID = FirebaseAuth.instance.currentUser!.uid;
FirebaseFirestore.instance
.collection('review')
.doc('productID_' + _productID)
.collection("userID_" + _userID)
.doc('review_data')
.set({
'stars': rating,
'title': _controllerReviewTitle.text,
'description': _controllerReviewDescription.text,
'image_url': downloadURLs,
});
//Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (context) => VerifyRegisterEmail()));
}
}
_checkInputValuesEmpty(context, text, field) {
if (text.toString().length == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Enter text in field ${field}'),
));
return false;
} else {
return true;
}
}
Can I give the container a dynamic height which has always the same height how the listview with my image names?
try find this place and add after Colum mainAxisAlignment: MainAxisAlignment.start,
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: customSubAppBar("Create Review", context),
body: SingleChildScrollView(
child: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
Related
I am setting up an animated profile page with NestedScrollView and slivers. But when I tap on the button to follow a store, it doesn't change until I scroll and I don't understand.
Here is the profile page :
Scaffold(
backgroundColor: primaryDark,
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverPersistentHeader(
delegate: SliverPersistentDelegate(
widget.user, state.products, follow, setState),
pinned: true,
),
// lets create a long list to make the content scrollable
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: SliverToBoxAdapter(
child: Column(
children: [
Container(
color: primaryDark,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.user.username,
style: TextStyle(
fontSize: 16,
color: primaryWhite,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 5),
Text(
widget.user.city.isEmpty
? widget.user.country
: (widget.user.city +
', ' +
widget.user.country),
style: TextStyle(
fontSize: 14,
color: primaryWhite,
),
),
const SizedBox(height: 5),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0),
child: StatefulBuilder(
builder: (context, state) {
return rowStars(
// callback: (){},
numberOfFullStars: widget.user.userNote,
type: "sellerStars",
colors: [primaryGreen, primaryDark],
sizes: [12.5, 7.5],
//
);
}),
),
const SizedBox(height: 15),
Divider(
height: 0,
color: primaryGrey4,
),
const SizedBox(height: 5),
],
),
),
],
),
),
),
];
},
body: _buildContent(
context,
width,
state.user,
state.products,
state.showMessage,
)),
);
and the SliverPersistentDelegate
class SliverPersistentDelegate extends SliverPersistentHeaderDelegate {
final UserModel user;
final List<Product> products;
bool follow;
final double maxHeaderHeight = 120;
final double minHeaderHeight = kToolbarHeight + 20;
final double maxImageSize = 63;
final double minImageSize = 0;
final StateSetter stateSetter;
SliverPersistentDelegate(
this.user, this.products, this.follow, this.stateSetter);
#override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
final size = MediaQuery.of(context).size;
final percent = shrinkOffset / (maxHeaderHeight - 65);
final percent2 = shrinkOffset / (maxHeaderHeight);
final currentImageSize = (maxImageSize * (1 - percent)).clamp(
minImageSize,
maxImageSize,
);
final currentImagePosition = ((size.width / 2 - 65) * (1 - percent)).clamp(
minImageSize,
maxImageSize,
);
return Container(
color: primaryDark,
child: Container(
color: primaryGrey7.withOpacity(percent2 * 2 < 1 ? percent2 * 2 : 1),
child: Stack(
children: [
Positioned(
top: MediaQuery.of(context).viewPadding.top + 15,
left: currentImagePosition + 55,
child: (user.isOfficial)
? Row(
mainAxisAlignment: MainAxisAlignment.start,
//crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user?.username ?? "",
style: TextStyle(
color: primaryWhite.withOpacity(percent2),
fontSize: 20,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
),
),
SizedBox(width: 2.0),
currentImageSize <= 8 && user.isOfficial
? SvgPicture.asset(
"assets/svg/certification.svg",
)
: SizedBox.shrink()
],
)
: Text(
user?.username ?? "",
style: TextStyle(
color: primaryWhite.withOpacity(percent2),
fontSize: 24,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
),
),
),
Positioned(
left: 15,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(
Icons.arrow_back_sharp,
size: 30,
color: primaryWhite,
),
),
),
Positioned(
right: 50,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: (userConnected != null &&
userConnected.id != null &&
user.id != userConnected.id)
? GestureDetector(
onTap: () {
if (follow) {
// unfollow event
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedEventUnfollowing(
user: user,
),
);
BlocProvider.of<FollowingBloc>(context).add(
FollowingDisplayStarted(
uid: userConnected.id,
),
);
stateSetter(
() {
follow = false;
},
);
} else {
// follow event
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedEventFollowing(user: user),
);
BlocProvider.of<FollowingBloc>(context).add(
FollowingDisplayStarted(
uid: userConnected.id,
),
);
stateSetter(
() {
follow = true;
},
);
}
},
child: SvgPicture.asset(
(follow)
? "assets/svg/icon_follow.svg"
: "assets/svg/icon_unfollow.svg",
width: 30,
height: 30,
),
)
: SizedBox.shrink()),
Positioned(
right: 7,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: (userConnected != null &&
userConnected.id != null &&
user.id != userConnected.id)
? GestureDetector(
onTap: () {
displayCupertinoDialogReport(
user,
MediaQuery.of(context).size.width,
context,
products,
);
},
child: Icon(
Icons.more_vert,
size: 30,
color: primaryWhite,
),
)
: SizedBox.shrink()),
Positioned(
left: currentImagePosition + 87,
top: MediaQuery.of(context).viewPadding.top + 15,
bottom: 0,
child: Hero(
tag: 'profile',
child: Container(
width: currentImageSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: user.isOfficial
? SvgPicture.asset(
'assets/svg/tag_faces_official.svg',
)
: SvgPicture.asset(
'assets/svg/tag_faces.svg',
)),
),
),
],
),
),
);
}
#override
double get maxExtent => maxHeaderHeight;
#override
double get minExtent => minHeaderHeight;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
POST: i tried to include a StateStater in the parameters to update the value of the "follow" variable but nothing.
The full page :
class AnimatedUserVisitedProfile extends StatefulWidget {
final User user;
const AnimatedUserVisitedProfile({Key key, this.user}) : super(key: key);
#override
State<AnimatedUserVisitedProfile> createState() =>
_AnimatedUserVisitedProfileState();
}
class _AnimatedUserVisitedProfileState
extends State<AnimatedUserVisitedProfile> {
Completer<void> _refreshCompleter;
#override
void initState() {
super.initState();
_refreshCompleter = Completer<void>();
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedEventClicked(
uid: widget.user.id,
),
);
}
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return BlocConsumer<ProfileVisitedBloc, ProfileVisitedState>(
listener: (context, state) {
if (state is ProfileVisitedStateLoaded) {
_refreshCompleter?.complete();
_refreshCompleter = Completer();
}
}, builder: (context, state) {
if (state is ProfileVisitedStateLoaded) {
if (state.showMessage != null && state.showMessage) {
Timer.periodic(Duration(seconds: 1), (timer) {
if (this.mounted) {
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedMessageDisappeared(),
);
Navigator.pop(context);
}
timer.cancel();
});
}
bool follow = (userConnected == null)
? false
: (widget.user != null &&
widget.user.followers.contains(userConnected.id))
? true
: false;
return Scaffold(
backgroundColor: primaryDark,
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverPersistentHeader(
delegate: SliverPersistentDelegate(
widget.user, state.products, follow, setState),
pinned: true,
),
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: SliverToBoxAdapter(
child: Column(
children: [
Container(
color: primaryDark,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.user.username,
style: TextStyle(
fontSize: 16,
color: primaryWhite,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 5),
Text(
widget.user.city.isEmpty
? widget.user.country
: (widget.user.city +
', ' +
widget.user.country),
style: TextStyle(
fontSize: 14,
color: primaryWhite,
),
),
const SizedBox(height: 5),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0),
child: StatefulBuilder(
builder: (context, state) {
return rowStars(
// callback: (){},
numberOfFullStars: widget.user.userNote,
type: "sellerStars",
colors: [primaryGreen, primaryDark],
sizes: [12.5, 7.5],
//
);
}),
),
const SizedBox(height: 15),
Divider(
height: 0,
color: primaryGrey4,
),
const SizedBox(height: 5),
],
),
),
],
),
),
),
];
},
body: _buildContent(
context,
width,
state.user,
state.products,
state.showMessage,
)),
);
}
return Center(
child: Container(
color: Colors.black,
child: loading(),
),
);
});
}
Widget _buildContent(
BuildContext context,
double width,
User user,
List<Product> products,
bool showMessage,
) {//return a ListView of products
}
class SliverPersistentDelegate extends SliverPersistentHeaderDelegate {
final UserModel user;
final List<Product> products;
bool follow;
final double maxHeaderHeight = 120;
final double minHeaderHeight = kToolbarHeight + 20;
final double maxImageSize = 63;
final double minImageSize = 0;
final StateSetter stateSetter;
SliverPersistentDelegate(
this.user, this.products, this.follow, this.stateSetter);
#override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
final size = MediaQuery.of(context).size;
final percent = shrinkOffset / (maxHeaderHeight - 65);
final percent2 = shrinkOffset / (maxHeaderHeight);
final currentImageSize = (maxImageSize * (1 - percent)).clamp(
minImageSize,
maxImageSize,
);
final currentImagePosition = ((size.width / 2 - 65) * (1 - percent)).clamp(
minImageSize,
maxImageSize,
);
return Container(
color: primaryDark,
child: Container(
color: primaryGrey7.withOpacity(percent2 * 2 < 1 ? percent2 * 2 : 1),
child: Stack(
children: [
Positioned(
top: MediaQuery.of(context).viewPadding.top + 15,
left: currentImagePosition + 55,
child: (user.isOfficial)
? Row(
mainAxisAlignment: MainAxisAlignment.start,
//crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user?.username ?? "",
style: TextStyle(
color: primaryWhite.withOpacity(percent2),
fontSize: 20,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
),
),
SizedBox(width: 2.0),
currentImageSize <= 8 && user.isOfficial
? SvgPicture.asset(
"assets/svg/certification.svg",
)
: SizedBox.shrink()
],
)
: Text(
user?.username ?? "",
style: TextStyle(
color: primaryWhite.withOpacity(percent2),
fontSize: 24,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
),
),
),
Positioned(
left: 15,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(
Icons.arrow_back_sharp,
size: 30,
color: primaryWhite,
),
),
),
Positioned(
right: 50,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: (userConnected != null &&
userConnected.id != null &&
user.id != userConnected.id)
? GestureDetector(
onTap: () {
if (follow) {
// unfollow event
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedEventUnfollowing(
user: user,
),
);
BlocProvider.of<FollowingBloc>(context).add(
FollowingDisplayStarted(
uid: userConnected.id,
),
);
stateSetter(
() {
follow = false;
},
);
} else {
// follow event
BlocProvider.of<ProfileVisitedBloc>(context).add(
ProfileVisitedEventFollowing(user: user),
);
BlocProvider.of<FollowingBloc>(context).add(
FollowingDisplayStarted(
uid: userConnected.id,
),
);
stateSetter(
() {
follow = true;
},
);
}
},
child: SvgPicture.asset(
(follow)
? "assets/svg/icon_follow.svg"
: "assets/svg/icon_unfollow.svg",
width: 30,
height: 30,
),
)
: SizedBox.shrink()),
Positioned(
right: 7,
top: currentImageSize == 0
? MediaQuery.of(context).viewPadding.top + 15
: MediaQuery.of(context).viewPadding.top + 33,
child: (userConnected != null &&
userConnected.id != null &&
user.id != userConnected.id)
? GestureDetector(
onTap: () {
displayCupertinoDialogReport(
user,
MediaQuery.of(context).size.width,
context,
products,
);
},
child: Icon(
Icons.more_vert,
size: 30,
color: primaryWhite,
),
)
: SizedBox.shrink()),
Positioned(
left: currentImagePosition + 87,
top: MediaQuery.of(context).viewPadding.top + 15,
bottom: 0,
child: Hero(
tag: 'profile',
child: Container(
width: currentImageSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: user.isOfficial
? SvgPicture.asset(
'assets/svg/tag_faces_official.svg',
)
: SvgPicture.asset(
'assets/svg/tag_faces.svg',
)),
),
),
],
),
),
);
}
#override
double get maxExtent => maxHeaderHeight;
#override
double get minExtent => minHeaderHeight;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
the flutter code below must show a view the problem is that when I run the code and click on the button to view the view index = 1 I get the following error:
The following NoSuchMethodError was thrown during a scheduler
callback: The getter 'data' was called on null. Receiver: null Tried
calling: data
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:68:5)
#1 ThemeProvider.of (package:animated_theme_switcher/src/theme_provider.dart:27:22)
now the error appears only in the display of this view, what is it due to and how can I solve it?
ChangeView.dart
..
//Function call new view with index=1
void onItemPressed(BuildContext context, {int index}) {
switch (index) {
case 0:
break;
case 1:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserScreen()
));
break;
}
}
..
User.dart
class UserScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
ScreenUtil.init(context,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
allowFontScaling: true);
var profileInfo = Expanded(
child: Column(
children: <Widget>[
Container(
height: kSpacingUnit.w * 10,
width: kSpacingUnit.w * 10,
margin: EdgeInsets.only(top: kSpacingUnit.w * 3),
child: Stack(
children: <Widget>[
CircleAvatar(
radius: kSpacingUnit.w * 5,
backgroundImage: AssetImage('assets/images/avatar.png'),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: kSpacingUnit.w * 2.5,
width: kSpacingUnit.w * 2.5,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
shape: BoxShape.circle,
),
child: Center(
heightFactor: kSpacingUnit.w * 1.5,
widthFactor: kSpacingUnit.w * 1.5,
child: Icon(
LineAwesomeIcons.pen,
color: kDarkPrimaryColor,
size: ScreenUtil().setSp(kSpacingUnit.w * 1.5),
),
),
),
),
],
),
),
SizedBox(height: kSpacingUnit.w * 2),
Text(
'Nicolas Adams',
style: kTitleTextStyle,
),
SizedBox(height: kSpacingUnit.w * 0.5),
Text(
'nicolasadams#gmail.com',
style: kCaptionTextStyle,
),
SizedBox(height: kSpacingUnit.w * 2),
Container(
height: kSpacingUnit.w * 4,
width: kSpacingUnit.w * 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(kSpacingUnit.w * 3),
color: Theme.of(context).accentColor,
),
child: Center(
child: Text(
'Upgrade to PRO',
style: kButtonTextStyle,
),
),
),
],
),
);
var themeSwitcher = ThemeSwitcher(
builder: (context) {
return AnimatedCrossFade(
duration: Duration(milliseconds: 200),
crossFadeState:
ThemeProvider.of(context).brightness == Brightness.dark
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: GestureDetector(
onTap: () =>
ThemeSwitcher.of(context).changeTheme(theme: kLightTheme),
child: Icon(
LineAwesomeIcons.sun,
size: ScreenUtil().setSp(kSpacingUnit.w * 3),
),
),
secondChild: GestureDetector(
onTap: () =>
ThemeSwitcher.of(context).changeTheme(theme: kDarkTheme),
child: Icon(
LineAwesomeIcons.moon,
size: ScreenUtil().setSp(kSpacingUnit.w * 3),
),
),
);
},
);
var header = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(width: kSpacingUnit.w * 3),
Icon(
LineAwesomeIcons.arrow_left,
size: ScreenUtil().setSp(kSpacingUnit.w * 3),
),
profileInfo,
themeSwitcher,
SizedBox(width: kSpacingUnit.w * 3),
],
);
return ThemeSwitchingArea(
child: Builder(
builder: (context) {
return Scaffold(
body: Column(
children: <Widget>[
SizedBox(height: kSpacingUnit.w * 5),
header,
Expanded(
child: ListView(
children: <Widget>[
UserListItem(
icon: LineAwesomeIcons.user_shield,
text: 'Privacy',
),
UserListItem(
icon: LineAwesomeIcons.history,
text: 'Purchase History',
),
UserListItem(
icon: LineAwesomeIcons.question_circle,
text: 'Help & Support',
),
UserListItem(
icon: LineAwesomeIcons.cog,
text: 'Settings',
),
UserListItem(
icon: LineAwesomeIcons.user_plus,
text: 'Invite a Friend',
),
UserListItem(
icon: LineAwesomeIcons.alternate_sign_out,
text: 'Logout',
hasNavigation: false,
),
],
),
)
],
),
);
},
),
);
}
}
UserListItem.dart
class UserListItem extends StatelessWidget {
final IconData icon;
final String text;
final bool hasNavigation;
const UserListItem({
Key key,
this.icon,
this.text,
this.hasNavigation = true,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: kSpacingUnit.w * 5.5,
margin: EdgeInsets.symmetric(
horizontal: kSpacingUnit.w * 4,
).copyWith(
bottom: kSpacingUnit.w * 2,
),
padding: EdgeInsets.symmetric(
horizontal: kSpacingUnit.w * 2,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(kSpacingUnit.w * 3),
color: Theme.of(context).backgroundColor,
),
child: Row(
children: <Widget>[
Icon(
this.icon,
size: kSpacingUnit.w * 2.5,
),
SizedBox(width: kSpacingUnit.w * 1.5),
Text(
this.text,
style: kTitleTextStyle.copyWith(
fontWeight: FontWeight.w500,
),
),
Spacer(),
if (this.hasNavigation)
Icon(
LineAwesomeIcons.angle_right,
size: kSpacingUnit.w * 2.5,
),
],
),
);
}
}
In your UserScreen inside ThemeSwitcher (where you pass the value in crossFadeState) use ThemeProvider like this
ThemeProvider.themeOf(context).brightness == Brightness.dark
i met a error on val... It says ,
A value of type 'String?' can't be assigned to a variable of type
'String'. Try changing the type of the variable, or casting the
right-hand type to 'String'.
got error on this ,
this code is in below full code
onChanged: (val) => setState(
() => _applyleavevalueChanged = val),
validator: (val) {
setState(
() => _applyleavevalueToValidate = val);
return null;
},
onSaved: (val) => setState(
() => _applyleavevalueSaved = val),
i met a error on val... It says ,
A value of type 'String?' can't be assigned to a variable of type
'String'. Try changing the type of the variable, or casting the
right-hand type to 'String'.
this is my full source code.....
File : leaveapply.dart
import 'package:date_time_picker/date_time_picker.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './LeaveApply/BouncingButton.dart';
import './LeaveApply/leave_history.dart';
import 'LeaveApply/LeaveHistoryCard.dart';
import 'LeaveApply/datepicker.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: LeaveApply(),
title: 'Leave Apply',
));
}
class LeaveApply extends StatefulWidget {
#override
_LeaveApplyState createState() => _LeaveApplyState();
}
class _LeaveApplyState extends State<LeaveApply>
with SingleTickerProviderStateMixin {
late Animation animation, delayedAnimation, muchDelayedAnimation, LeftCurve;
late AnimationController animationController;
final searchFieldController = TextEditingController();
late TextEditingController _applyleavecontroller;
String _applyleavevalueChanged = '';
String _applyleavevalueToValidate = '';
String _applyleavevalueSaved = '';
late TextEditingController _fromcontroller;
String _fromvalueChanged = '';
String _fromvalueToValidate = '';
String _fromvalueSaved = '';
late TextEditingController _tocontroller;
String _tovalueChanged = '';
String _tovalueToValidate = '';
String _tovalueSaved = '';
#override
void initState() {
// ignore: todo
// TODO: implement initState
super.initState();
//SystemChrome.setEnabledSystemUIOverlays([]);
_applyleavecontroller =
TextEditingController(text: DateTime.now().toString());
_fromcontroller = TextEditingController(text: DateTime.now().toString());
_tocontroller = TextEditingController(text: DateTime.now().toString());
animationController =
AnimationController(duration: Duration(seconds: 3), vsync: this);
animation = Tween(begin: -1.0, end: 0.0).animate(CurvedAnimation(
parent: animationController, curve: Curves.fastOutSlowIn));
delayedAnimation = Tween(begin: 1.0, end: 0.0).animate(CurvedAnimation(
parent: animationController,
curve: Interval(0.2, 0.5, curve: Curves.fastOutSlowIn)));
muchDelayedAnimation = Tween(begin: -1.0, end: 0.0).animate(CurvedAnimation(
parent: animationController,
curve: Interval(0.3, 0.5, curve: Curves.fastOutSlowIn)));
}
#override
void dispose() {
// ignore: todo
// TODO: implement dispose
animationController.dispose();
super.dispose();
}
final GlobalKey<FormState> _formkey = new GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
animationController.forward();
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;
final GlobalKey<ScaffoldState> _scaffoldKey =
new GlobalKey<ScaffoldState>();
return AnimatedBuilder(
animation: animationController,
builder: (BuildContext context, Widget? child) {
final GlobalKey<ScaffoldState> _scaffoldKey =
new GlobalKey<ScaffoldState>();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Apply Leave',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
leading: new IconButton(
icon: new Icon(
Icons.arrow_back_ios,
color: Colors.black87,
size: 18.0,
),
onPressed: () => Navigator.of(context).pop(),
),
centerTitle: true,
elevation: 1.0,
),
body: Form(
key: _formkey,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
color: Colors.black.withOpacity(0.5),
height: 1,
),
SizedBox(
height: height * 0.05,
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Text(
"Apply Leave Date",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Padding(
padding: EdgeInsets.only(
top: 13,
),
child: Container(
// height: height * 0.06,
padding: EdgeInsets.only(
left: 10,
),
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(5),
),
child: Row(
children: [
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Container(
width: width * 0.75,
child: DateTimePicker(
type: DateTimePickerType.date,
dateMask: 'dd/MM/yyyy',
controller: _applyleavecontroller,
//initialValue: _initialValue,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
calendarTitle: "Leave Date",
confirmText: "Confirm",
enableSuggestions: true,
//locale: Locale('en', 'US'),
onChanged: (val) => setState(
() => _applyleavevalueChanged = val),
validator: (val) {
setState(
() => _applyleavevalueToValidate = val);
return null;
},
onSaved: (val) => setState(
() => _applyleavevalueSaved = val),
),
),
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: Icon(
Icons.calendar_today,
color: Colors.black,
),
),
],
),
),
),
SizedBox(
height: height * 0.03,
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Text(
"Choose Leave Type",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 14,
),
),
),
SizedBox(
height: height * 0.02,
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: DropdownSearch<String>(
validator: (v) => v == null ? "required field" : null,
hint: "Please Select Leave type",
mode: Mode.MENU,
showSelectedItems: true,
items: [
"Medical",
"Family",
"Sick",
'Function',
'Others'
],
showClearButton: true,
onChanged: print,
),
),
SizedBox(
height: height * 0.05,
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Text(
"Leave Date",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Padding(
padding: EdgeInsets.only(
top: 13,
),
child: Container(
// height: height * 0.06,
padding: EdgeInsets.only(
left: 10,
),
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white38,
borderRadius: BorderRadius.circular(5),
boxShadow: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
)
]),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Icon(
Icons.calendar_today,
color: Colors.black,
),
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Container(
padding: const EdgeInsets.only(left: 4.0),
width: width * 0.28,
decoration: BoxDecoration(
color: Colors.white38,
boxShadow: [
BoxShadow(
offset: Offset(0, 1),
blurRadius: 2,
color: Colors.black26,
)
]),
child: CustomDatePicker(
controller: _fromcontroller,
title: "From",
onchanged: (val) =>
setState(() => _fromvalueChanged = val),
validator: (val) {
setState(
() => _fromvalueToValidate = val);
return null;
},
saved: (val) =>
setState(() => _fromvalueSaved = val),
),
),
),
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Icon(
Icons.arrow_forward,
color: Colors.black,
),
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Container(
padding: const EdgeInsets.only(left: 4.0),
width: width * 0.28,
decoration: BoxDecoration(
color: Colors.white38,
boxShadow: [
BoxShadow(
offset: Offset(0, 1),
blurRadius: 2,
color: Colors.black26,
)
],
),
child: CustomDatePicker(
controller: _tocontroller,
title: "To",
onchanged: (val) => setState(() {
_tovalueChanged = val;
print(val);
}),
validator: (val) {
setState(() => _tovalueToValidate = val);
return null;
},
saved: (val) =>
setState(() => _tovalueSaved = val),
),
),
),
),
],
),
),
),
SizedBox(
height: height * 0.05,
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Text(
"Apply Leave Date",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: Padding(
padding: EdgeInsets.only(
top: 13,
),
child: Container(
// height: height * 0.06,
height: height * 0.25,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(5),
),
child: TextFormField(
//autofocus: true,
minLines: 1,
maxLines: 10,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
suffixIcon: searchFieldController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear),
onPressed: () => WidgetsBinding.instance
?.addPostFrameCallback((_) =>
searchFieldController.clear()))
: null,
border: InputBorder.none,
contentPadding: EdgeInsets.all(7),
),
),
),
),
),
SizedBox(
height: height * 0.05,
),
Transform(
transform: Matrix4.translationValues(
muchDelayedAnimation.value * width, 0, 0),
child: Text(
"Attach Document",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {},
child: Text(
"Click 📑 to Upload Documents",
style: TextStyle(
color: Color(0xff2876bd),
),
),
),
),
),
SizedBox(
height: 15,
),
Transform(
transform: Matrix4.translationValues(
delayedAnimation.value * width, 0, 0),
child: Bouncing(
onPress: () {},
child: Container(
//height: 20,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Color(0xff2876bd),
),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Request Leave",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
),
),
SizedBox(
height: 50,
),
Column(
children: [
InkWell(
child: Text(
'LeaveHistory',
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LeaveHistory(
// leavetype: '',
// description: '',
// status: '',
// requesteddate: '',
// verifieddate: '',
)),
);
},
)
],
),
SizedBox(
height: 50,
)
],
),
),
),
),
);
},
);
}
}
The value type of _applyleavevalueChanged is String and
the value type of val is String?
if val can be null
_applyleavevalueChanged = val ?? ''; // '' is the default value in case val is null
or
if (val != null) {
_applyleavevalueChanged = val!; // ! mark that the val is definitely not null
}
The error you get came from null-safety, the type String? means that it could be either a String, or null, but your variable only accepts a String, and no null.
For this, you can "force" the use of 'non-null' variable by adding a ! at the end of your variable, but be careful when doing this.
_applyleavevalueToValidate = val!
You can learn more about null-safety syntax and principles on the official documentation: https://flutter.dev/docs/null-safety.
https://api.flutter.dev/flutter/material/DropdownButton-class.html.
You can simply set default value like this. For example,
String? a;
String x = a ?? "default value";
how can i convert listview to reorderedableListview to make a priority task app
this is my application design output
i see many solutions but in most of them i found error
Here is initstate code
class _TodoListScreenState extends State<TodoListScreen> {
late List<Task> taskList = [];
#override
void initState() {
super.initState();
_updateTaskList();
}
_updateTaskList() async {
print('--------->update');
this.taskList = await DatabaseHelper.instance.getTaskList();
print(taskList);
setState(() {});
}
this is method where listtile created
Widget _buildTask(Task task) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: ListTile(
title: Text(
task.title!,
style: TextStyle(
fontSize: 18.0,
decoration: task.status == 0
? TextDecoration.none
: TextDecoration.lineThrough,
),
),
subtitle: Text(
'${DateFormat.yMMMEd().format(task.date!)} • ${task.priority}',
style: TextStyle(
fontSize: 18.0,
decoration: task.status == 0
? TextDecoration.none
: TextDecoration.lineThrough,
),
),
trailing: Checkbox(
onChanged: (value) {
task.status = value! ? 1 : 0;
DatabaseHelper.instance.updateTask(task);
_updateTaskList();
},
value: task.status == 1 ? true : false,
activeColor: Theme.of(context).primaryColor,
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddTask(
task: task,
updateTaskList: () {
_updateTaskList();
},
),
),
),
),
),
Divider(),
],
);
}
this is method build
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddTask(updateTaskList: _updateTaskList),
),
),
),
in this body tag i want to create reorderable listview
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 80.0),
itemCount: taskList.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Tasks",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 40.0,
color: Colors.black),
),
SizedBox(height: 15.0),
Text(
'${taskList.length} of ${taskList.where((Task task) => task.status == 1).toList().length} task complete ',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20.0,
color: Colors.grey,
),
),
],
),
);
} else {
return _buildTask(taskList[index - 1]);
}
},
),
);
}
}
this is whole code i want to change
There is a widget like ReorderableListView and library like Reorderables are available that you can use.
Updated
Sample Code:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:reorderables/reorderables.dart';
class ReorderablesImagesPage extends StatefulWidget {
ReorderablesImagesPage({
Key key,
this.title,
#required this.size,
}) : super(key: key);
final String title;
final double size;
#override
State<StatefulWidget> createState() => _ReorderablesImagesPageState();
}
class _ReorderablesImagesPageState extends State<ReorderablesImagesPage> {
List<Widget> _tiles;
int maxImageCount = 30;
double iconSize;
final int itemCount = 3;
final double spacing = 8.0;
final double runSpacing = 8.0;
final double padding = 8.0;
#override
void initState() {
super.initState();
iconSize = ((widget.size - (itemCount - 1) * spacing - 2 * padding) / 3)
.floor()
.toDouble();
_tiles = <Widget>[
Container(
child: Image.network('https://picsum.photos/250?random=1'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=2'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=3'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=4'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=5'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=6'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=7'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=8'),
width: iconSize,
height: iconSize,
),
Container(
child: Image.network('https://picsum.photos/250?random=9'),
width: iconSize,
height: iconSize,
),
];
}
#override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
setState(() {
Widget row = _tiles.removeAt(oldIndex);
_tiles.insert(newIndex, row);
});
}
var wrap = ReorderableWrap(
minMainAxisCount: itemCount,
maxMainAxisCount: itemCount,
spacing: spacing,
runSpacing: runSpacing,
padding: EdgeInsets.all(padding),
children: _tiles,
onReorder: _onReorder,
onNoReorder: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
},
onReorderStarted: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder started: index:$index');
});
var column = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: wrap,
),
),
ButtonBar(
buttonPadding: EdgeInsets.all(16),
alignment: MainAxisAlignment.end,
children: <Widget>[
if (_tiles.length > 0)
IconButton(
iconSize: 50,
icon: Icon(Icons.remove_circle),
color: Colors.teal,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
_tiles.removeAt(0);
});
},
),
if (_tiles.length < maxImageCount)
IconButton(
iconSize: 50,
icon: Icon(Icons.add_circle),
color: Colors.deepOrange,
padding: const EdgeInsets.all(0.0),
onPressed: () {
var rand = Random();
var newTile = Container(
child: Image.network(
'https://picsum.photos/250?random=${rand.nextInt(100)}'),
width: iconSize,
height: iconSize,
);
setState(() {
_tiles.add(newTile);
});
},
),
],
),
],
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: column,
);
}
}
I've got a new transaction screen with several textfields and a container at the bottom for shortcuts:
And when i trigger a textfield the grey container moves up as well:
Here is my code for the screen:
//import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
//import 'package:flutter/services.dart';
import '../items/icon_picket_item.dart';
import '../items/select_transaction_item.dart';
import '../providers/user_transactions.dart';
import '../providers/icon_auswahl.dart';
import '../providers/account_type.dart';
import '../providers/user_settings/single_multiple_acc.dart';
import 'package:provider/provider.dart';
import '../storag/locale.dart';
import '../storag/foundation.dart';
import '../my_icons.dart';
class NewTransactionScreen extends StatefulWidget {
static const routeName = '/new-transaction';
#override
_NewTransactionScreenState createState() => _NewTransactionScreenState();
}
class _NewTransactionScreenState extends State<NewTransactionScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final _titleController = TextEditingController(text: '');
final _amountController = TextEditingController(text: '-10.00');
final _accountController = TextEditingController();
final _notesController = TextEditingController();
final _repeatController = TextEditingController();
DateTime _selectedDate;
Color _correct = Colors.white54;
final data = [];
void _colorCorrect() {
final enteredTitle = _titleController.text;
final enteredAmount = _amountController.text;
final enteredAccount = _accountController.text;
final enteredRepeat = _repeatController.text;
Color color;
if (enteredTitle.isEmpty ||
enteredAmount.isEmpty ||
enteredAccount.isEmpty ||
enteredRepeat.isEmpty) {
color = Colors.white54;
} else {
color = Colors.white;
}
setState(() {
_correct = color;
});
}
void _submitData(String choiceId, NewTransactions transactions, iconData,
GeldKonto kontoss) {
//onSubmitted gives you a string
if (_amountController.text.isEmpty) {
return;
}
final enteredTitle = _titleController.text;
final enteredAmount = _amountController.text;
final enteredAccount = _accountController.text;
final enteredNotes = _notesController.text;
final enteredRepeat = _repeatController.text;
Icon enteredIcon = iconData.taken;
if (enteredTitle.isEmpty ||
enteredAmount.isEmpty ||
enteredAccount.isEmpty ||
enteredRepeat.isEmpty) {
return; //means code stops here and addTx doesnt run
} else {
transactions.addNewtransaction(
xTitle: enteredTitle,
xAmount: double.parse(enteredAmount) <= 0
? double.parse(enteredAmount) * (-1)
: double.parse(enteredAmount),
xchosenDate: _selectedDate == null ? DateTime.now() : _selectedDate,
xAccountType: enteredAccount,
xNotes: enteredNotes,
xRepeat: enteredRepeat,
xIcon: enteredIcon,
xId: DateTime.now().toString(),
xchoiceId: choiceId);
}
Navigator.of(context).pop(context);
// .pop closes modalsheet , you have the property contexyt cause of extends state
}
Container _buildTransactionRow(
{Text typedText,
Function onPress,
comparisonStuff,
Icon icon,
double iconSpace,
double iconAfterSpace}) {
final mediaQuery = MediaQuery.of(context);
return Container(
// padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(height: 1, width: iconSpace),
icon,
SizedBox(height: 1, width: iconAfterSpace), //25
SizedBox(
width: mediaQuery.size.width * 0.75,
child: GestureDetector(
onTap: onPress,
child: Column(
//crossAxisAlignment: CrossAxisAlignment.,
children: <Widget>[
SizedBox(height: 17, width: 1),
SizedBox(
width: mediaQuery.size.width * 0.75,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 1,
width: 4,
),
typedText
],
),
),
SizedBox(height: 10, width: mediaQuery.size.width * 0.04),
SizedBox(
height: 1,
width: mediaQuery.size.width * 0.75,
child: const Divider(
color: Colors.white54,
thickness: 1,
),
),
],
),
),
),
],
),
);
}
Widget _buildAppBar(String pageTitle, NewTransactions transactions,
GeldKonto kontoss, String choiceId) {
return AppBar(
automaticallyImplyLeading: false,
leading: isIos == true
? IconButton(
icon: Icon(CupertinoIcons.back, size: 30, color: Colors.white),
onPressed: () => Navigator.of(context).pop(context),
)
: IconButton(
icon: Icon(Icons.arrow_back, size: 30, color: Colors.white),
onPressed: () => Navigator.of(context).pop(context),
),
centerTitle: true,
title: Text(
pageTitle,
style: const TextStyle(
fontSize: 25,
color: Colors.white,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.end,
),
actions: <Widget>[
Consumer<IconAuswahl>(
builder: (ctx, iconData, child) => IconButton(
padding: EdgeInsetsDirectional.only(
start: 15,
end: 25,
top: 5,
bottom: 0,
),
icon: Icon(
MyIcon.correct,
size: 45,
color: _correct,
),
onPressed: () =>
_submitData(choiceId, transactions, iconData, kontoss),
),
),
],
backgroundColor: Color(0xffb00374a),
);
}
Widget _buildColumn(
BuildContext context, AppBar appBar, local, String choiceId) {
final mediaQuery = MediaQuery.of(context);
return Stack(children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [Colors.black, Color(0xffb00374a)],
),
),
),
Positioned(
bottom: 0,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.grey),
),
SingleChildScrollView(
child: Container(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top ) * 1.05 ,
padding: EdgeInsets.only(
top: 10,
left: 20,
right: 10,
bottom:0,
//
// so that the whole thing always move +10
),
child: Consumer<SinlgeOrMultiple>(
builder: (_, data, __) => data.multipleAcc == true
? Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
// CupertinoTextField(
// ),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Bankkonto'
: 'Mein Ausgewähltes Zeug',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.account, size: 38),
iconSpace: 3,
iconAfterSpace: 20,
onPress: () {}),
Row(children: <Widget>[
SizedBox(width: 3.5),
choiceId == '0'
? const Icon(
MyIcon.paying,
color: Colors.white,
size: 25,
)
: const Icon(
MyIcon.earning,
color: Colors.white,
size: 43,
),
const SizedBox(width: 33),
SelectTransactionItem(
choiceId == '0' //id of new outcome
? '-${oCcyy.format(0)}'
: '${oCcyy.format(0)}',
_amountController,
false,
_colorCorrect),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
IconPicked(_scaffoldKey, choiceId),
const SizedBox(
width:
14),
SelectTransactionItem(
'Titel',
_titleController,
true,
_colorCorrect),
],
),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Jede 2 Woche'
: 'Ausgewählter Stuff',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.runningtime, size: 40),
iconSpace: 0,
iconAfterSpace: 20,
onPress: () {}),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? '${DateFormat.yMd(local).format(DateTime.now())}'
: '${DateFormat.yMMMd().format(_selectedDate)}',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.calender, size: 39),
iconSpace: 3,
iconAfterSpace: 18,
onPress: () {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: MediaQuery.of(context)
.copyWith()
.size
.height /
3,
child: CupertinoDatePicker(
//backgroundColor: Color(0xffb00374a),
initialDateTime: DateTime.now(),
onDateTimeChanged:
(DateTime pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
},
// use24hFormat: true,
maximumDate: DateTime.now(),
minimumYear: 2010,
maximumYear: 2020,
//minuteInterval: 1,
mode: CupertinoDatePickerMode.date,
),
);
});
},
),
Row(
children: <Widget>[
const SizedBox(width: 3.5),
const Icon(
MyIcon.notes,
color: Colors.white,
size: 37,
),
const SizedBox(width: 20),
SelectTransactionItem('Notizen', _notesController,
false, _colorCorrect),
],
),
Container(
width: 0.1,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.transparent),
])
: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Row(children: <Widget>[
const SizedBox(width: 3.5),
choiceId == '0'
? const Icon(
MyIcon.paying,
color: Colors.white,
size: 25,
)
: const Icon(
MyIcon.earning,
color: Colors.white,
size: 43,
),
const SizedBox(width: 33),
SelectTransactionItem(
choiceId == '0' //id of new outcome
? '-${oCcyy.format(0)}'
: '${oCcyy.format(0)}',
_amountController,
false,
_colorCorrect),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
IconPicked(_scaffoldKey, choiceId),
const SizedBox(
width:
14),
SelectTransactionItem(
'Titel',
_titleController,
true,
_colorCorrect),
],
),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Jede 2 Woche'
: 'Ausgewählter Stuff',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.runningtime, size: 40),
iconSpace: 0,
iconAfterSpace: 20,
onPress: () {}),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? '${DateFormat.yMd(local).format(DateTime.now())}'
: '${DateFormat.yMMMd().format(_selectedDate)}',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.calender, size: 39),
iconSpace: 3,
iconAfterSpace: 18,
onPress: () {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: MediaQuery.of(context)
.copyWith()
.size
.height /
3,
child: CupertinoDatePicker(
//backgroundColor: Color(0xffb00374a),
initialDateTime: DateTime.now(),
onDateTimeChanged:
(DateTime pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
},
// use24hFormat: true,
maximumDate: DateTime.now(),
minimumYear: 2010,
maximumYear: 2020,
//minuteInterval: 1,
mode: CupertinoDatePickerMode.date,
),
);
});
},
),
Row(
children: <Widget>[
const SizedBox(width: 3.5),
const Icon(
MyIcon.notes,
color: Colors.white,
size: 37,
),
const SizedBox(width: 20),
SelectTransactionItem('Notizen', _notesController,
false, _colorCorrect),
],
),
Container(
width: 0.1,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.transparent),
])),
),
),
]);
}
#override
Widget build(BuildContext context) {
var local = Localizations.localeOf(context).toString();
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final _pageTitle = routeArgs['pageTitle'];
final _choiceId = routeArgs['id'];
final transactions = Provider.of<NewTransactions>(context, listen: false);
final kontoss = Provider.of<GeldKonto>(context, listen: false);
final PreferredSizeWidget appBar =
_buildAppBar(_pageTitle, transactions, kontoss, _choiceId);
return
// SafeArea(
// child:
Scaffold(
key: _scaffoldKey,
appBar: appBar,
// body: Stack(
// children: <Widget>[
body: _buildColumn(context, appBar, local, _choiceId),
//),
);
}
}
It is fine when i open the upper textfield but the container overlap when opening the last textfield. I can scroll up to see the notes ,,textfield'', but it looks ugly when I open the notes ,,textfield'' and the thing is overlapping. Is there a way to move the textfield up automatically or just preventing that the Container is pushed up as well?
I tried to put the padding of the textfield column Container to + 10, but it squeezed the whole page.
padding: EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: MediaQuery.of(context).viewInsets.bottom +
10, // so that the whole thing always move +10
),
Do you have any advice to solve the problem?
Try adding resizeToAvoidBottomInset: false to your scaffold
Try wrapping Scaffold widget with a Stack and then put the Container widget after Scaffod
If you need to hide some widgets when keyboard is visible, you can use this package: https://pub.dev/packages/flutter_keyboard_visibility
Example:
KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
return isKeyboardVisible
? const SizedBox()
: const MyButton();
}),
I don't like using resizeToAvoidBottomPadding: false, because, when you have a text field, it doesn't allow to move the text field to the top of keyboard.