Related
I have built a Flutter application using MobX as my state management and am running into a strange issue at the moment that is only present on Android running in release mode.
I'm not sure if the offender is MobX, Hive or just Flutter in Android itself. However, on this specific page in my app, the Obsever will only display the last entry in the list. The other items are present, but the UI will only show the last index of the list. When I turn my phone landscape, the full content of the list is then visible and the page displays exactly as intended. Is there a way I can force the widgets to re-render in MobX when the page has already loaded?
I have tried downgrading my target SDK to 28, downgrading the gradle version, setting shrinkResources & minifyEnabled to false, enabled proguard. I also have ensured to call this in my main.dart;
WidgetsFlutterBinding.ensureInitialized();
Also attached are the outputs of my flutter doctor.
Again, this issue is only present on Android release build of my app. It works perfectly on iOS & Android on debug.
Any help would be greatly appreciated.
Below is the widget in question that has issues rendering in release mode
Widget _carPackageList() {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: viewModel.customerCars.length,
itemBuilder: (context, index) => _carListItem(index));
}
Widget _carListItem(index) {
var packageDetails = viewModel.getDetailBookingById(
viewModel.customerCars[index].packageGroupId);
return Observer(
builder: (context) {
if (viewModel.isLoading) {
return CustomProgressIndiactor();
} else {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
children: [
Text(
viewModel.customerCars[index].makeAndModel,
maxLines: 2,
style: TextStyle(
fontSize: 25,
fontFamily: 'Domus',
color: Color(0xff3c99a0))),
],
),
),
IconButton(
icon: Icon(Icons.highlight_remove, color: Colors.black),
onPressed: () {
viewModel.removeCustomerCar(index);
viewModel.customerCars.removeAt(index);
})
],
),
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(15.0),
child: Text(packageDetails.name,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(
fontSize: 25,
fontFamily: 'Domus',
color: Color(0xff3c99a0))),
),
],
),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Observer(
builder: (_) {
var groupPrice =
viewModel.calculateCarCost(
viewModel.customerCars[index],
viewModel.customerCars[index]
.packageGroupId);
if (groupPrice == null) {
return Text('£0.00',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontWeight: FontWeight.w700));
} else {
return Text(
'£${groupPrice.toStringAsFixed(2)}',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontWeight: FontWeight.w700),
);
}
},
),
),
],
),
Padding(
padding: const EdgeInsets.only(
left: 15.0, top: 0, right: 15),
child: Text(packageDetails.description,
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontSize: 16,
fontWeight: FontWeight.w300)),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Optional Extras',
style: GoogleFonts.lato(
fontSize: 16,
color: Colors.black,
fontWeight: FontWeight.bold)),
Observer(
builder: (_) {
var opExtraPrice = viewModel
.calculateOptionalExtrasPrice(viewModel
.customerCars[index]
.optionalExtras);
if (opExtraPrice == null) {
return Text('£0.00',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontWeight: FontWeight.w700));
} else {
return Text(
'£${opExtraPrice.toStringAsFixed(2)}',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontWeight: FontWeight.w700));
}
},
),
],
),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: viewModel.customerCars[index]
.optionalExtras.length,
itemBuilder: (context, innerIndex) {
var detailedOptionalExtra =
viewModel.getOptinalExtraById(viewModel
.customerCars[index]
.optionalExtras[innerIndex]
.packageItemId);
return Text(
detailedOptionalExtra.name,
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontSize: 16,
fontWeight: FontWeight.w300),
);
},
)),
Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Total Cost:',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontSize: 16,
fontWeight: FontWeight.w700),
),
Observer(
builder: (context) => Text(
'£${viewModel.currentTotalCost.toStringAsFixed(2)}',
style: TextStyle(
color: Color(0xff1A2E35),
fontFamily: 'Aileron',
fontWeight: FontWeight.w700))),
],
),
)
],
),
),
),
],
),
);
}
},
);
}
The solution I found was to generate the list directly from the Hive box using a ValueListenableBuilder to listen to the box and add more elements to the list as soon as they arrive in the box. I can only assume there was some kind of race case going on with MobX attempting to gather the elements in the box from Hive and then serve up to the UI layer. I'll attach some sample code below for anyone else who may run into a similar issue.
Widget buildList() {
var _box = Hive.box("myBox").listenable();
return ValueListenableBuilder(
valueListenable: _box,
builder: (context, box, widget) {
return ListView.builder(
itemCount: box.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 0.1))),
child: ListTile(
title: Text(Hive.box("myBox").getAt(index)),
),
);
},
);
});
}
I'm trying to replicate a flutter design that looks like this, I was able to achieve something similar by using Listtile widget, my only issue was that the leading icon didn't align vertically with the title. Please is there any other way to achieve this? I tried using a row widget but the multiple line text won't fit into the screen.
You can try this code
Container(
color: Colors.white,
padding: const EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
child: Icon(
Icons.message,
color: Colors.white,
),
),
SizedBox(width: 10,),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Message", style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16
),),
Text("How to create row with multiple lines in flutter How to create row with multiple lines in flutter")
],
),
),
],
),
),
Result:-
Please let me know if it work.
You can try as well as my code.
Widget ListItem({
String imageUrl,
String text,
String caption,
double imageSize = 50,
double horizontalSpacing = 15,
double verticalspacing = 20,
bool showDetailIndicatorIcon = true,
GestureTapCallback onTap,
}) {
return InkWell(
onTap: onTap,
child: Container(
margin: EdgeInsets.only(top: 2),
padding: EdgeInsets.symmetric(
horizontal: horizontalSpacing, vertical: verticalspacing),
child: Row(
children: [
ClipRRect(
child: Image.network(
imageUrl,
fit: BoxFit.cover,
height: imageSize,
width: imageSize,
),
borderRadius: BorderRadius.circular(imageSize / 2),
),
Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
text,
style: TextStyle(
color: ColorRes.lightWhite,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 5),
Text(
caption,
style: TextStyle(
color: ColorRes.grey,
fontSize: 12,
fontWeight: FontWeight.normal,
),
maxLines: 3,
),
],
),
),
),
],
),
),
);
}
I hope it's work. Please let me know.
I have this Text
I want when I clicked on add Icon to refresh 30 to 31
this is my code :
Row(
children: <Widget>[
Expanded(
child: Container(
height: 30.0,
width: 30.0,
child: Image.asset('assets/images/trash_can.png'),
),
),
Expanded(
child: Center(
child: Text(
mealsNum.toString(),
style: TextStyle(
fontSize: 20.0
),
),
),
),
Expanded(
child: GestureDetector(
onTap: (){
mealsNum++;
print(mealsNum);
},
child: Icon(
Icons.add,
color: Color(0xffFFD243),
),
),
),
],
),
How can I do this?
use a set state to update
onTap: (){
setState((){
mealsNum++;
});
print(mealsNum);,
You need to add setState in onTap
Row(
children: <Widget>[
Expanded(
child: Container(
height: 30.0,
width: 30.0,
child: Image.asset('assets/images/trash_can.png'),
),
),
Expanded(
child: Center(
child: Text(
mealsNum.toString(),
style: TextStyle(
fontSize: 20.0
),
),
),
),
Expanded(
child: GestureDetector(
onTap: (){
setState(() {
mealsNum++;
});
print(mealsNum);
},
child: Icon(
Icons.add,
color: Color(0xffFFD243),
),
),
),
],
),
setSate is used for rebuilding the widgets. Add setstate in onTap
Row(
children: <Widget>[
Expanded(
child: Container(
height: 30.0,
width: 30.0,
child: Image.asset('assets/images/trash_can.png'),
),
),
Expanded(
child: Center(
child: Text(
mealsNum.toString(),
style: TextStyle(
fontSize: 20.0
),
),
),
),
Expanded(
child: GestureDetector(
onTap: (){
setState(() {
mealsNum++;
});
print(mealsNum);
},
child: Icon(
Icons.add,
color: Color(0xffFFD243),
),
),
),
],
),
you should learn state management in flutter here is the link to learn the basic from flutter basic doc after you read this you maybe ask what about global state or more complicated state mangement then you can choose provider or for more advance use flutter_block here is the link for flutter_block library made with example
,have a good luck with state management
I am using the PageView for the view-pager functionality and showing the image on each page with contains some text and button on each page.
As I am using the Stack widget for putting the view on one another like Relative-layout in Android, But My Image on the First Page of the PageView is not set to fit on the screen
I have tried the below solution like below
1).First link
2). Second Link
But not getting the proper solution, I have tried the below lines of code for it,
class MyTutorialScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Material(
child: PageIndicatorContainer(
pageView: PageView(
children: <Widget>[
Stack(
//////ON THIS SECTION I AM GETTIG PROBLEM
children: <Widget>[
Container(
/////// NEED THE SOLUTION ON THIS SECTION
color: Colors.yellow,
child: Image.asset('images/walkthrough_1.png',
fit: BoxFit.fitWidth),
),
Positioned(
top: 40.0,
right: 40.0,
child: InkWell(
onTap: () {
print("HERE IS I AM");
},
child: Text(
"SKIP",
style: TextStyle(color: Colors.white),
),
)),
Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Welcome",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"To The Ravindra Kushwaha App. \n One tap Club Destination !",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center),
),
],
),
)
],
),
Container(
color: Colors.yellow,
child: Image.asset('images/walkthrough_2.png', fit: BoxFit.cover),
),
Container(
color: Colors.blueAccent,
child: Image.asset('images/walkthrough_3.png', fit: BoxFit.cover),
)
],
),
length: 3,
align: IndicatorAlign.bottom,
indicatorSpace: 5.0,
indicatorColor: Colors.white,
indicatorSelectorColor: Colors.purpleAccent,
padding: EdgeInsets.all(10.0),
),
);
}
}
And please check the screen which I am getting from the above code
Above the screen, how would I remove the white color on the right side?
I am using this library for the indicator
page_indicator: ^0.2.1
Please use below line to show image as per device width and height..Try below lines of code and let me know in case of concern
Container(
height: double.maxFinite, //// USE THIS FOR THE MATCH WIDTH AND HEIGHT
width: double.maxFinite,
child:
Image.asset('images/walkthrough_3.png', fit: BoxFit.fill),
)
I have implemented the ViewPager (PageView) functionality , by below lines of code, I am posting the answer for the other which can be helpful for the future user.
I have used this library for the indicator like below
page_indicator: ^0.2.1
Please check the below lines of code for it
import 'package:flutter/material.dart';
import 'package:page_indicator/page_indicator.dart';
class MyTutorialScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Material(
child: PageIndicatorContainer(
pageView: PageView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[
Stack(
children: <Widget>[
Container(
height: double.maxFinite,
width: double.maxFinite,
child:
Image.asset('images/walkthrough_1.png', fit: BoxFit.fill),
),
Positioned(
top: 40.0,
right: 40.0,
child: InkWell(
onTap: () {
print("HERE IS I AM");
},
child: Text(
"SKIP",
style: TextStyle(color: Colors.white),
),
)),
Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Welcome",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"To The Ravindra Kushwaha App. \n One tap Club Destination !",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center),
),
],
),
)
],
),
Stack(
children: <Widget>[
Container(
height: double.maxFinite,
width: double.maxFinite,
child:
Image.asset('images/walkthrough_2.png', fit: BoxFit.fill),
),
Positioned(
top: 40.0,
right: 40.0,
child: InkWell(
onTap: () {
print("HERE IS I AM");
},
child: Text(
"SKIP",
style: TextStyle(color: Colors.white),
),
)),
Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Welcome",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"To The Ravindra Kushwaha App. \n One tap Club Destination !",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center),
),
],
),
)
],
),
Stack(
children: <Widget>[
Container(
height: double.maxFinite,
width: double.maxFinite,
child:
Image.asset('images/walkthrough_3.png', fit: BoxFit.fill),
),
Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Welcome",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"To The Ravindra Kushwaha App. \n One tap Club Destination !",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center),
),
Container(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text("Let's GO!!"),
onPressed: () {},
),
),
],
),
)
],
)
],
),
length: 3,
align: IndicatorAlign.bottom,
indicatorSpace: 5.0,
indicatorColor: Colors.white,
indicatorSelectorColor: Colors.purpleAccent,
padding: EdgeInsets.all(10.0),
),
);
}
}
I'd like to add a "Sign in with Google" Button to my Flutter app. This button should be in accordance with the terms of Google.
My problem is, that the button I've create looks really awful.
I used the images which Google provides on their website but I don't know if I'm doing right with the code for the button.
Widget _createLoginButtonGoogle() {
return new Expanded(
child: new Container(
margin: EdgeInsets.fromLTRB(30.0, 5.0, 30.0, 5.0),
child: new RaisedButton(
color: const Color(0xFF4285F4),
shape: _createButtonBorder(),
onPressed: () {},
child: new Row(
children: <Widget>[
new Image.asset(
'res/images/icons/google/btn_google_dark_normal_mdpi.9.png',
height: 48.0,
),
new Expanded(
child: _createButtonText('Sign in with Google', Colors.white),
),
],
),
),
),
);
}
What I want is that my button looks like the original Google button
And not like my version
Can anyone tell me how to create the original google button? Is there a better way than creating a RaisedButton?
There Is A Pretty Awesome Package Called flutter_signin_button on pub.dev.
You Can Use It It Has Sign In Buttons For
Email
Google
Facebook
GitHub
LinkedIn
Pinterest
Tumblr
Twitter
Apple
With Some Supporting Dark Mode Also With Mini Buttons!
First Add It To Your pubspec.yaml
dependencies:
...
flutter_signin_button:
then import it into your file:
import 'package:flutter_signin_button/flutter_signin_button.dart';
and use the buttons like this:
SignInButton(
Buttons.Google,
onPressed: () {},
)
Here Is A Preview Of All The Buttons:
I'm giving you a general idea, replace Android icon with your Google image using Image.asset(google_image)
InkWell(
onTap: () {},
child: Ink(
color: Color(0xFF397AF3),
child: Padding(
padding: EdgeInsets.all(6),
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Icon(Icons.android), // <-- Use 'Image.asset(...)' here
SizedBox(width: 12),
Text('Sign in with Google'),
],
),
),
),
)
you can use Padding property of raised button also use property of Row widget and mainAxisSize of button. Following code may help you to understand clearly.
new Container(
margin: EdgeInsets.fromLTRB(30.0, 5.0, 30.0, 5.0),
child: new RaisedButton(
padding: EdgeInsets.only(top: 3.0,bottom: 3.0,left: 3.0),
color: const Color(0xFF4285F4),
onPressed: () {},
child: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Image.asset(
'res/images/icons/google/btn_google_dark_normal_mdpi.9.png',
height: 48.0,
),
new Container(
padding: EdgeInsets.only(left: 10.0,right: 10.0),
child: new Text("Sign in with Google",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),)
),
],
)
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.white,
onPrimary: Colors.black,
),
onPressed: () {
googleLogin();
},
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Image(
image: AssetImage("assets/google_logo.png"),
height: 18.0,
width: 24,
),
Padding(
padding: EdgeInsets.only(left: 24, right: 8),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 20,
color: Colors.black54,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
),
for more you can change the Text font to Roboto Medium and make the size 14x, based on the branding rule https://developers.google.com/identity/branding-guidelines#font
use this one
child: InkWell(
onTap: () {},
child: Container(
height: 50,
width: CustomWidth(context) - 100,
color: Colors.blueAccent,
child: Row(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 3),
height: 45,
width: 50,
decoration: BoxDecoration(
color: Colors.white,
image: DecorationImage(
image: NetworkImage("https://cdn-icons-png.flaticon.com/512/2991/2991148.png"))),
),
SizedBox(
width: 10,
),
DefaultTextView(
text: "SignUp With Google",
color: AppColors.whiteColor,
fontweight: FontWeight.bold,
)
],
),
),
),
You can use this class and just need to pass function and add google icon in your assets/images folder and change the icon name in code, and you are good to go.
import 'package:flutter/material.dart';
import 'package:social_app/constants/colors.dart';
class GoogleSignInButton extends StatefulWidget {
final function;
final String buttonText;
const GoogleSignInButton(
{super.key, required this.function, required this.buttonText});
#override
State<StatefulWidget> createState() => GoogleSignInButtonState();
}
class GoogleSignInButtonState extends State<GoogleSignInButton> {
bool isButtonClicked = false;
#override
Widget build(BuildContext context) {
return Container(
height: 50,
width: double.infinity,
child: ElevatedButton(
onPressed: isButtonClicked
? () {}
: () async {
setState(() {
isButtonClicked = true;
});
await widget.function();
setState(() {
isButtonClicked = false;
});
},
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(EdgeInsets.zero),
shape: isButtonClicked
? MaterialStateProperty.all<CircleBorder>(CircleBorder())
: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
)),
backgroundColor: MaterialStateProperty.all<Color>(mainColor),
),
child: isButtonClicked
? Container(
height: 25,
width: 25,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: Container(
padding: EdgeInsets.all(5),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(3)),
color: Colors.white,
),
padding: EdgeInsets.all(5),
child: Image.asset("assets/images/google_icon.png"),
),
SizedBox(
width: 30,
),
Text(
"Sign in with google",
style: TextStyle(fontSize: 20),
)
],
),
),
),
);
}
}
For google logo image on white background and raised button with blue background:
Container(
margin: EdgeInsets.fromLTRB(30.0, 5.0, 30.0, 5.0),
child: new RaisedButton(
padding: EdgeInsets.all(1.0),
color: const Color(0xff4285F4),
onPressed: () async {
_signInWithGoogle();
},
child: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
),
child: Image.asset(
Images.googleLogo,
height: 18.0,
),
),
Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0),
child: new Text(
"Sign in with Google",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
)),
],
)),
),