flutter app freezes if animation is added inside futureBuilder - flutter

I have this application where I will have futurebuilder to get data first, and then return a scaffold with a separate splash screen to simulate the screen darken effect with alertdialog while enabling setstate (I tested, they dont allow setstate with alert dialog). However, when I used Animated Opacity, Color Tween or Animated Switcher, as soon as the screen loads, the app freezes.
code:
class MainPageState extends State<MainPage> with TickerProviderStateMixin {
GlobalKey imageKey = GlobalKey();
late Animation splashAnimation;
late AnimationController splashController;
double splashOpacity = 0;
#override
void initState() {
splashController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
splashAnimation =
ColorTween(begin: Colors.transparent, end: Color.fromARGB(155, 0, 0, 0))
.animate(splashController);
super.initState();
}
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
PageLoader pageLoader = context.watch<PageLoader>();
TabProperty tabProperty = context.watch<TabProperty>();
return WillPopScope(
onWillPop: () async {
//check if user logged in ? disable back btn : exit app
bool login = false;
await StorageManager.readData("loggedIn")
.then((value) => login = value);
if (login) {
return false;
} else {
SystemNavigator.pop();
}
return false;
},
child: FutureBuilder(
future: getMenuAndAccountInfo(pageLoader),
builder: ((context, snapshot) {
//placeholder
if (!snapshot.hasData) {
return Stack(children: [
SafeArea(
child: Scaffold(
body: pageLoader.currentPage,
),
),
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Color.fromARGB(100, 0, 0, 0),
),
Center(child: CircularProgressIndicator()),
]);
} else {
List<dynamic> menuData =
(snapshot.data as Map<String, dynamic>)["Menu"];
return SafeArea(
child: RepaintBoundary(
key: imageKey,
child: Stack(
children: [
Scaffold(
body: pageLoader.currentPage,
bottomNavigationBar: Container(
height: height * 0.07,
color: Colors.blue,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: MaterialButton(
padding: EdgeInsets.zero,
splashColor: Colors.orange,
highlightColor: Colors.transparent,
onPressed: () {
setState(() {});
splashOpacity = 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.bookmark,
color: Colors.white,
size: height * 0.035,
),
Text(
"Bookmark",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02),
),
],
)),
),
Expanded(
child: MaterialButton(
padding: EdgeInsets.zero,
splashColor: Colors.orange,
highlightColor: Colors.transparent,
onPressed: () {
setState(() {});
splashOpacity = 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.account_circle_rounded,
color: Colors.white,
size: height * 0.035,
),
Text(
"Profile",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02),
)
],
)),
),
],
),
),
),
Positioned.fill(
child: AnimatedOpacity(
opacity: splashOpacity,
duration: Duration(milliseconds: 400),
child: Container(
color: Color.fromARGB(155, 0, 0, 0),
),
),
)
],
),
),
);
}
}),
),
);
}

NEVER build the future as the future: parameter to the FutureBuilder!
future: getMenuAndAccountInfo(pageLoader),
No!
Instead, you want to declare a variable to hold the future in State, and in initState, initialize that variable, and in your FutureBuilder, reference that variable.
For details, see the first few paragraphs of the FutureBuilder documentation, or see my video on that.

Related

Customize agora video call UI in flutter

my video call output
when logged another person to this video call then shows like this.But I want when someone logged to video call then my video should be small.
like this.
and also when click my video then my video should be big and another person's video should be small. how to do that ? I couldn't find the any documentation how to do that
Code video call ui
// video view
Widget _viewRows() {
final views = _getRenderViews();
switch (views.length) {
case 1:
return Column(
children: <Widget>[_videoView(views[0])],
);
case 2:
return Column(
children: <Widget>[
_expandedVideoRow([views[0]]),
_expandedVideoRow([views[1]])
],
);
case 3:
return Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 3))
],
);
case 4:
return Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 4))
],
);
default:
}
return Container();
}
How customize the video UI like as I mentioned?
error
To create the layout which you want, edit _viewRow(CallNotifier notifier) and _expandedVideoRow(List views) function with following code : -
Widget _viewRows(CallNotifier notifier) : -
case 2:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Stack(
children: [
_expandedVideoRow([views[secondScreen]]),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 10, bottom: 10),
child: GestureDetector(
onTap: () {
tempSwap = firstScreen;
firstScreen = secondScreen;
secondScreen = tempSwap;
setState(() {});
},
child: SizedBox(
height: 200,
width: 100,
child: _expandedVideoRow([views[firstScreen]])),
),
),
),
],
));
Above code contains _expandedVideoRow([views[secondScreen]]), which is just a simple Expandable Container and we are passing the index of the screen as a parameter. In our case, there are 2 screens hence 2 index that is 0 and 1. I have declared three integer variables here, int firstScreen = 0, int secondScreen = 1 and int tempSwap = 0. The second _expandedVideoRow([views[firstScreen]]) is wrapped by GesutreDector, so when the user taps on that screen the indexes of the variable are swapped which results in swapping the screens, SizedBox to reduce its width and height and Align widget to give the desired position to the second screen.
Widget _expandedVideoRow(List views) : -
Widget _expandedVideoRow(List<Widget> views) {
final wrappedViews = views.map<Widget>(_videoView).toList();
return Row(
children: wrappedViews,
);
}
Remove the Expanded widget that wraps the row like the above code because we can't use the Expanded under the Stack widget.
If you wish to change the bottom icons, then change _toolbar(CallNotifier notifier) function according to your need.
Widget _toolbar(CallNotifier notifier) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: () {
_onToggleMute(notifier);
setState(() {
isMute = !isMute;
});
},
child: Icon(
isMute ? Icons.mic_off : Icons.mic,
color: isMute ? Colors.white : Colors.teal,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: isMute ? Colors.teal : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: () => _onCallEnd(context),
child: Icon(
Icons.call_end,
color: Colors.white,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
),
],
),
);
}
Code which I use in my app, full code : -
class _CallScreenState extends State<CallScreen> {
double globalHeight;
int firstScreen = 0;
int secondScreen = 1;
int tempSwap = 0;
bool isMute = false;
void initState() {
super.initState();
}
List<Widget> _getRenderViews(CallNotifier model) {
final List<StatefulWidget> list = [];
list.add(RtcLocalView.SurfaceView());
model.users
.forEach((int uid) => list.add(RtcRemoteView.SurfaceView(uid: uid)));
return list;
}
Widget _videoView(view) {
return Expanded(child: Container(child: view));
}
Widget _expandedVideoRow(List<Widget> views) {
final wrappedViews = views.map<Widget>(_videoView).toList();
return Row(
children: wrappedViews,
);
}
Widget _viewRows(CallNotifier notifier) {
final views = _getRenderViews(notifier);
switch (views.length) {
case 1:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Column(
children: <Widget>[_videoView(views[0])],
));
case 2:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Stack(
children: [
_expandedVideoRow([views[secondScreen]]),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 10, bottom: 10),
child: GestureDetector(
onTap: () {
tempSwap = firstScreen;
firstScreen = secondScreen;
secondScreen = tempSwap;
setState(() {});
},
child: SizedBox(
height: 200,
width: 100,
child: _expandedVideoRow([views[firstScreen]])),
),
),
),
],
));
default:
}
return Container();
}
Widget _toolbar(CallNotifier notifier) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: () {
_onToggleMute(notifier);
setState(() {
isMute = !isMute;
});
},
child: Icon(
isMute ? Icons.mic_off : Icons.mic,
color: isMute ? Colors.white : Colors.teal,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: isMute ? Colors.teal : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: () => _onCallEnd(context),
child: Icon(
Icons.call_end,
color: Colors.white,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
),
],
),
);
}
void _onCallEnd(BuildContext context) {
Navigator.pop(context);
}
void _onToggleMute(CallNotifier notifier) {
notifier.isMuted = notifier.isMuted;
notifier.engine.muteLocalAudioStream(notifier.isMuted);
}
#override
Widget build(BuildContext context) {
return BaseWidget<CallNotifier>(
model: CallNotifier(),
onModelReady: (model) => model.init(widget.channelName, widget.token),
builder: (context, notifier, child) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Stack(
children: <Widget>[
_viewRows(notifier),
Align(
alignment: Alignment.bottomCenter,
child: _toolbar(notifier)),
],
),
),
);
});
}
}

Flutter Hide/Show text in a Flashcard

Here is my code:
import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:transformer_page_view/transformer_page_view.dart';
class Body extends StatefulWidget {
#override
_Body createState() => _Body();
}
class _Body extends State<Body> {
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
bool _visible = false;
return FutureBuilder<String>(
future: rootBundle.loadString('assets/files/questions.csv'),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
List<List<dynamic>> csvTable =
CsvToListConverter().convert(snapshot.data);
return Scaffold(
appBar: AppBar(
title: Text("Flashcards"),
),
body: new TransformerPageView(
loop: true,
viewportFraction: 0.8,
transformer: new PageTransformerBuilder(
builder: (Widget child, TransformInfo info) {
return new Padding(
padding: new EdgeInsets.all(10.0),
child: new Material(
elevation: 8.0,
textStyle: new TextStyle(color: Colors.white),
borderRadius: new BorderRadius.circular(10.0),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new Positioned(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ParallaxContainer(
child: new Text(
csvTable[info.index + 1][0],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: FloatingActionButton(
onPressed: () {
// Call setState. This tells Flutter to rebuild the
// UI with the changes.
setState(() {
_visible = !_visible;
});
},
tooltip: 'Toggle Opacity',
child: Icon(Icons.flip),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: new Text(
csvTable[info.index + 1][1],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
),
position: info.position,
translationFactor: 400.0,
),
],
),
left: 10.0,
right: 10.0,
top: 100.0,
)
],
),
),
);
}),
itemCount: csvTable.length - 1),
);
});
}
}
The excel file has the following data in it:
Question,
Command to sort lines of text files?,Answer 1
What does the 3rd column of /etc/fstab show?,Answer 2
Alternative name and location for grub.conf on some systems?,Answer 3
What the standard BASH file descriptors?,Answer 4
The idea is that when I click the button the answer shows (Flashcard concept). However, clicking the button is not doing anything. Please for the answers, I have looked everywhere and I know that visibility can be used; however, I had the same behavior when I used it
I think this is because your visible bool flag is declared inside the build function. So every time you setState, the visible flag is set equal to false. You can move it as a global bool field, then it should keep what you change when you clicked the button.
class _Body extends State<Body> {
bool _visible = false; ///Put it here
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return FutureBuilder<String>(
future: rootBundle.loadString('assets/files/questions.csv'),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
List<List<dynamic>> csvTable =
CsvToListConverter().convert(snapshot.data);
return Scaffold(
appBar: AppBar(
title: Text("Flashcards"),
),
body: new TransformerPageView(
loop: true,
viewportFraction: 0.8,
transformer: new PageTransformerBuilder(
builder: (Widget child, TransformInfo info) {
return new Padding(
padding: new EdgeInsets.all(10.0),
child: new Material(
elevation: 8.0,
textStyle: new TextStyle(color: Colors.white),
borderRadius: new BorderRadius.circular(10.0),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new Positioned(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ParallaxContainer(
child: new Text(
csvTable[info.index + 1][0],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: FloatingActionButton(
onPressed: () {
// Call setState. This tells Flutter to rebuild the
// UI with the changes.
setState(() {
_visible = !_visible;
});
},
tooltip: 'Toggle Opacity',
child: Icon(Icons.flip),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: new Text(
csvTable[info.index + 1][1],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
),
position: info.position,
translationFactor: 400.0,
),
],
),
left: 10.0,
right: 10.0,
top: 100.0,
)
],
),
),
);
}),
itemCount: csvTable.length - 1),
);
});
}
}

Flutter : how to hide and show button on last and first index in listView

i set two buttons(left and right Button) on top of ListView. buttons work for scrolling on click. now i want to hide the left button when index is 0 and the right button when index is last. more explain to clear, the left button will be hidden in first index and the right button will be hidden in last index. please help me.
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
final _controller = ScrollController();
var _width = 100.0;
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Colors.green,
)),
Expanded(
flex: 2,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
},
),
),
),
),
Expanded(
flex: 2,
child: Container(
color: Colors.transparent,
)),
Padding(
padding: const EdgeInsets.only(right: 8),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_forward)),
onTap: () {
_animateToIndex(recentIndexIncrease);
},
),
),
),
),
],
)),
Expanded(
flex: 16,
child: Container(
// height: 400,
child: ListView.builder(
controller: _controller,
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: word_data.drink.length,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index]
.replaceAll(" ", "_")
.toLowerCase();
return Container(
child: Column(
children: <Widget>[
Expanded(
flex: 6,
child: GestureDetector(
child: Container(
color: Colors.purple,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
"asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
),
)),
],
),
width: sizeDevice.width,
);
}),
color: Colors.yellow,
),
),
],
),
),
);
}
_animateToIndex(i) => _controller.animateTo(_width * i,
duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
this image of (ListView with top two Button)
I think it might be easier for you to replace ListView.builder by a Flutter_Swiper it will make your life a lot easier. Or maybe you can add a listner to your ScrollController in the initState where it handles the value of two Boolean variables leftButtonEnabled and rightButtonEnabled and set them to true or false depending on the position of the Controller
EDIT :
here's an example of using Flutter swiper in your code, I tried to make it simple and in the same time adding multiple features that can help you ( like SwiperControl ) I hope it helps you a little bit.
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: ScrollingLeftAndRightButtonHide(),
),
);
}
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();
}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
SwiperController _controller = SwiperController();
SwiperControl _control = SwiperControl(color: Colors.white);
double get _width => MediaQuery.of(context).size.width;
double get _height => MediaQuery.of(context).size.height;
bool inFirstPage = true;
bool inLastPage = false;
List<String> word_data = [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Last",
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
children: <Widget>[
Container(
color: Colors.white,
child: Row(
children: <Widget>[
if (!inFirstPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.previous();
},
icon: Icon(Icons.arrow_back),
),
Spacer(),
if (!inLastPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.next();
},
icon: Icon(Icons.arrow_forward),
),
],
),
),
Expanded(
child: Container(
color: Colors.white,
child: Swiper(
controller: _controller,
control: _control,
loop: false,
scrollDirection: Axis.horizontal,
itemCount: word_data.length,
onIndexChanged: (value) {
if (value == word_data.length - 1)
setState(() {
inLastPage = true;
});
else if (value == 0)
setState(() {
inFirstPage = true;
});
else {
setState(() {
inFirstPage = false;
inLastPage = false;
});
}
},
itemBuilder: (BuildContext context, int index) {
return Container(
child: Column(
children: <Widget>[
Expanded(
child: GestureDetector(
child: Container(
width: _width,
alignment: Alignment.center,
color: Colors.indigoAccent,
child: Text(word_data[index]),
),
),
),
],
),
);
},
),
),
),
],
),
),
);
}
}
Add two variables in your state as
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
bool leftEnabled = false; //this is initial visibility of left button
bool rightEnabled = true; //this is initial visibility of right button
........
Then in your build function where you are displaying left and right button add if statement
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
.............
if(leftEnabled)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
if (index == 0) { //Your logic to detect start of the list.
leftEnabled = false; //if it is the start make left invisible
}
if(list.size > 1)
rightEnabled = true; //whenever left button is pressed and your data has more than 1 element make right visible
setState(() {});
},
),
),
),
),
...............
Same code for the right button.
You cannot do it through your current structure of code. To achieve it you will have to move those arrow button Icons inside of the listView like this:
return ListView.builder(
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index].replaceAll(" ", "_").toLowerCase();
return Column(
children: <Widget>[
Row(
children: [
//Left arrow is the button indicating left arrow
if (index != 0) LeftArrow,
//Rightarrow is the button indicating right arrow
if (index != 4) RightArrow
],
),
Expanded(
child: GestureDetector(
child: Container(
color: Colors.purple,
padding: const EdgeInsets.all(10.0),
child: Image.asset("asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
)),
],
);
});

Flutter :- Custom showDialog with background Color

void _showDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
I need to create this event where at the moment of showing the dialogue window, the other part of the screen changes color, I am having trouble consulting other sources and the truth is that I cannot coincide with the event.
The goal is to achieve something like this:
just pass barrierColor param to showDialog
showDialog(
context: context,
barrierColor: Color(0x640000FF),
builder: (BuildContext context) {
return AlertDialog(
// ...
);
},
);
You just need to show another screen which act as the dialog on the previous screen. For it first you need to use the Stack widget for it. I have created the demo one, please check it once.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutterlearningapp/colors.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomeScreen();
}
}
class _HomeScreen extends State<HomeScreen> {
bool isDialog = false;
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: SafeArea(
top: true,
bottom: true,
child: Stack(
children: <Widget>[
Container(
color: Colors.white,
child: Align(
alignment: Alignment.center,
child: Column(
children: <Widget>[
Container(
color: Colors.pink,
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.4,
),
SizedBox(
height: 10.0,
),
RaisedButton(
onPressed: () {
setState(() {
isDialog = true;
});
},
child: Text("Open Dialog")),
],
),
)),
Positioned.fill(
child: Align(
alignment: Alignment.centerRight,
child: isDialog ? transparentWidget(context) : Container(),
),
)
],
)));
}
Widget transparentWidget(BuildContext context) {
return Container(
color: const Color(0x4D2980b9),
width: double.infinity,
height: double.infinity,
child: Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(40.0),
topRight: const Radius.circular(40.0),
bottomLeft: const Radius.circular(40.0),
bottomRight: const Radius.circular(40.0),
)),
child: Center(
child: Text("Your sheet"),
),
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.8,
),
RaisedButton(
onPressed: () {
setState(() {
isDialog = false;
});
},
child: Text("Cancel"))
],
)),
);
}
}
And thee output of the above program as follow, please check it

How to stop a widget to reload on setState() method in StatefulWidget Class in Flutter

What I am trying to achieve is to save the state of the Widget i.e it should not refresh when the setState() method is been called.
class _GenderSelectionPageState extends State<GenderSelectionPage> {
bool isFemaleSelected = false;
AdmobBannerSize bannerSize;
GlobalKey _globalKey = new GlobalKey();
bool isLoaded = false;
#override
void initState() {
// TODO: implement initState
super.initState();
bannerSize = AdmobBannerSize.BANNER;
}
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return new Scaffold(
body: new Container(
width: width,
child: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: new Hero(
tag: "gender",
child: Material(
child: new Row(
children: <Widget>[
InkWell(
onTap: () {
setState(() {
if (isFemaleSelected) {
isFemaleSelected = false;
} else {
isFemaleSelected = true;
}
});
},
child: Opacity(
child: Image.asset(
"assets/woman.png",
height: height / 4,
width: width / 2 - 12,
),
opacity: isFemaleSelected ? 1.0 : 0.30,
),
),
InkWell(
onTap: () {
setState(() {
if (isFemaleSelected) {
isFemaleSelected = false;
} else {
isFemaleSelected = true;
}
});
},
child: Opacity(
opacity: !isFemaleSelected ? 1.0 : 0.30,
child: Image.asset(
"assets/boy.png",
height: height / 4,
width: width / 2 - 12,
),
),
),
],
),
),
),
),
],
),
flex: 1,
),
InkWell(
onTap: () {
setState(() {
});
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext) =>
new HeightWeightSelection(isFemaleSelected
? "assets/woman.png"
: "assets/boy.png")));
},
child: Container(
margin: EdgeInsets.only(bottom: 12.0),
child: new Image.asset(
"assets/next.png",
height: 64.0,
width: 64.0,
),
)),
new AdmobBannerWrapper(adUnitId: getBannerAdUnitId(),adSize: bannerSize,key: _globalKey,),
/* new AdmobBanner(
adUnitId: getBannerAdUnitId(),
adSize: bannerSize,
listener:
(AdmobAdEvent event, Map<String, dynamic> args) {
handleEvent(event, args, 'Banner');
},
),*/
],
),
)),
);
}
I don't want to call the AdmobBannerWrapper every time I press my image button at the bottom.AdmobBannerWrapper should be loaded once only but the thing is whenever I click the Next Image it load AdmobBannerWrapper method every time.
Build it in initState() then use it's reference wherever required it will not build again until parent widget reinitialized.
var banner;
#override
void initState() {
// TODO: implement initState
super.initState();
bannerSize = AdmobBannerSize.BANNER;
banner = AdmobBannerWrapper(adUnitId: getBannerAdUnitId(),adSize: bannerSize,key:_globalKey,);
}
than call it in by reference here banner
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return new Scaffold(
body: new Container(
width: width,
child: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: new Hero(
tag: "gender",
child: Material(
child: new Row(
children: <Widget>[
InkWell(
onTap: () {
setState(() {
if (isFemaleSelected) {
isFemaleSelected = false;
} else {
isFemaleSelected = true;
}
});
},
child: Opacity(
child: Image.asset(
"assets/woman.png",
height: height / 4,
width: width / 2 - 12,
),
opacity: isFemaleSelected ? 1.0 : 0.30,
),
),
InkWell(
onTap: () {
setState(() {
if (isFemaleSelected) {
isFemaleSelected = false;
} else {
isFemaleSelected = true;
}
});
},
child: Opacity(
opacity: !isFemaleSelected ? 1.0 : 0.30,
child: Image.asset(
"assets/boy.png",
height: height / 4,
width: width / 2 - 12,
),
),
),
],
),
),
),
),
],
),
flex: 1,
),
InkWell(
onTap: () {
setState(() {
});
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext) =>
new HeightWeightSelection(isFemaleSelected
? "assets/woman.png"
: "assets/boy.png")));
},
child: Container(
margin: EdgeInsets.only(bottom: 12.0),
child: new Image.asset(
"assets/next.png",
height: 64.0,
width: 64.0,
),
)),
banner, /*this is our variable */
/* new AdmobBanner(
adUnitId: getBannerAdUnitId(),
adSize: bannerSize,
listener:
(AdmobAdEvent event, Map<String, dynamic> args) {
handleEvent(event, args, 'Banner');
},
),*/
],
),
)),
);
}
Put it inside a stateless widget:
class AdBanner extends StatelessWidget{
final adUnitId;
final adSize;
const AdBanner({Key key, this.adUnitId, this.adSize}) : super(key: key);
Widget build(BuildContext context){
return AdmobBannerWrapper(
adUnitId: getBannerAdUnitId(),
adSize: bannerSize,
key: _globalKey,
);
}
}
next time when you call setState if the parameters are unchanged, it is not going to get rebuilt.