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
Related
I want to stack two bottom sheet each other in flutter as show in photo. The upper one is shown when in error state. In photo, it build with alert dialog. I want is with bottom sheet. How can I get it?
Edit:
Here is my code that I want to do. Lower bottom sheet is with pin field, autoComplete. autoComplete trigger StreamController, and then streamBuilder watch Error state and show dialog.
confirmPasswordModalBottomSheet(
BiometricAuthRegisterBloc biometricAuthRegBloc) {
showMaterialModalBottomSheet(
context: context,
builder: (BuildContext context) {
return StreamBuilder(
stream: biometricAuthRegBloc.biometricAuthRegisterStream,
builder: (context,AsyncSnapshot<ResponseObject>biometricAuthRegSnapShot) {
if (biometricAuthRegSnapShot.hasData) {
if (biometricAuthRegSnapShot.data!.messageState ==
MessageState.requestError) {
showModalBottomSheet(context: context, builder:
(BuildContext context){
return Container(
width: 200,height: 200,
child: Center(child: Text('Helllllllllo'),),);
});
}
}
return SizedBox(
width: 100,
height: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: margin30,
),
Text(CURRENT_PIN_TITLE),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.only(
left: margin60, right: margin60),
child: PinCodeField(
pinLength: 6,
onChange: () {},
onComplete: (value) {
biometricAuthRegBloc.biometricAuthRegister(
biometricType:_biometricAuthTypeForApi,
password: value);
},
),
),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal:
margin80),
child: AppButton(
onClick: () {},
label: CANCEL_BTN_LABEL,
),
),
Container(
padding: const EdgeInsets.all(8.0),
margin:
EdgeInsets.symmetric(vertical: 8.0,
horizontal: 30),
decoration: BoxDecoration(
color: Colors.grey,
border: Border.all(color: Colors.black),
),
child: const Text(
FINGER_PRINT_DIALOG,
textAlign: TextAlign.center,
),
)
],
),
);
});
},
);
}
When I do like that above, I get setState() or markNeedsBuild() called during build. Error and why? Sorry for my previous incomplete question.
I am bit confused with your question but stacking two bottomsheet is just easy. You just need to call the showModalBottomSheet whenever you want it shown to user. You can check out the following implementation:
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ElevatedButton(
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 500,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 1'),
ElevatedButton(
child: const Text('Show second modal 2'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.redAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 2'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
),
],
),
),
);
},
);
},
),
],
),
),
);
},
);
},
child: Text('Show bottom sheet 1'),
),
);
}
}
I have solution. All I need to do is, need to add WidgetBinding.insatance.addPostFrameCallback((timeStamp){showModalBottomSheet()}); in the StreamBuilder return.
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.
I have an image and I want it to appear on my screen, above my background, with keeping background a bit dark type like in the image. I am thinking on using AlertDialog to display the image, but if you think there is a better way of doing it or a specific widget for this, please do tell me. Also please tell me what do we name this kind of image which hovers over background and focusing itself in UI.
enter code here
Just for trying out, I used this in my screen's initstate, as I want it to appear as soon as my screen appears -
super.initState();
showDialog(
context: context,
builder: (BuildContext buildContext) {
return AlertDialog(
content: Center(
child: Container(
color: Colors.red,
width: 200,
height: 200,
),
),
);
}
);
initState doesn't contain context you can get context after first frame.
You can use addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
showDialog(..);}
Or Future.delayed
Future.delayed(Duration.zero).then((value) {
showDialog(..); }
The dialog construction is :
showDialog(
context: context,
builder: (BuildContext buildContext) {
const double iconSize = 48;
return LayoutBuilder(
builder: (context, constraints) => AlertDialog(
backgroundColor: Colors.transparent,
elevation: 0,
contentPadding: EdgeInsets.zero,
content: SizedBox(
width: constraints.maxWidth * .75,
height: constraints.maxHeight * .75,
child: Stack(
children: [
///background image with fit Cover
Align(
alignment: Alignment.center,
child: Container(
width: constraints.maxWidth * .75 - iconSize,
height: constraints.maxHeight * .75 - iconSize,
color: Colors.cyanAccent,
),
),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
decoration: const BoxDecoration(
color: Colors.white, shape: BoxShape.circle),
child: const Icon(
Icons.close,
size: iconSize,
),
),
),
)
],
),
),
),
);
},
);
You can use Stack widget withe three widget as children.
first children widget is your background screen.
second children widget is a Container widget with color .withOpacity().
third children widget is the image you are trying to show on top.
Like this:
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.white,
),
Container(
color: Colors.black.withOpacity(0.5),
),
Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
),
),
],
),
);
for Visibility issue you can use this.
bool isVisible = false;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.red,
child: Center(
child: Container(
child: TextButton(
onPressed: () {
setState(() {
isVisible = true;
});
},
child: Text('Show Widget'),
),
),
),
),
Visibility(
visible: isVisible,
child: GestureDetector(
onTap: () {
setState(() {
isVisible = false;
});
},
child: Container(
color: Colors.white.withOpacity(0.5),
),
),
),
Visibility(
visible: isVisible,
child: Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
child: Stack(
children: [
Positioned(
top: 0.0,
right: 0.0,
child: IconButton(
icon: const Icon(
Icons.close,
color: Colors.white,
),
onPressed: () {
setState(() {
isVisible = false;
});
},
),
),
],
),
),
),
),
],
),
);
}
What is the best way to get a responsive svg image, I thought about using MediaQuery, but probably not quite it will fit for every screen.
I used Stack and Positioned because I have more things to lay on one screen that will overlap.
I want to make responsive this:
class Shape extends StatelessWidget {
static Route route() {
return MaterialPageRoute<void>(builder: (_) => Shape());
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () => Navigator.of(context).pop(),
),
),
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return SafeArea(
child: Align(
child: Center(
child: Stack(children: <Widget>[
Positioned(
width: MediaQuery.of(context).size.width * 1,
height: MediaQuery.of(context).size.height,
top: MediaQuery.of(context).size.width * 0.4,
child: _curved(context),
),
]),
),
),
);
// });
}
Widget _curved(BuildContext context) {
return SvgPicture.asset(
'assets/images/shape_curved.svg',
color:Colors.green,
allowDrawingOutsideViewBox: true,
);}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
Flexible(
flex: 2,
child: Container(
color: Colors.yellow,
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(80.0),
))),
),
),
Flexible(
child: Container(
color: Colors.green,
child: Container(
decoration: BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(80.0),
))),
),
),
]),
);
}
}
I am quite new to flutter and I've been trying to set up a profile page for my app but it seems like I don't quite understand how these flutter widgets come together, I apologize if my question is quite dumb but I've been googling and failing at getting this to work. So basically I have a scaffold which holds a container thats supposed to display profile info (email, name, etc.), under it I'd like to place a listview, but flutter has been boggling my mind, I can't seem to understand how layouts work together, here is my code. When I try to do buildPage(), I get an error that the scaffold has infinite size, using buildBox() alone works. I'm not sure how to go about this. Any Help is appreciated
import 'package:flutter/material.dart';
class ProfileBox extends StatefulWidget {
final String userEmail;
const ProfileBox(this.userEmail);
#override
_ProfileBoxState createState() => _ProfileBoxState();
}
class _ProfileBoxState extends State<ProfileBox> {
#override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildBox(){
return Scaffold(
body: Align(
alignment: Alignment.topCenter,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
margin: const EdgeInsets.only(top: 20.0),
decoration: BoxDecoration(
color: Color(0xFF8185E2), border: Border.all(
color: Color(0xFF8185E2),
),
borderRadius: BorderRadius.all(Radius.circular(20))
),
height: constraints.maxHeight / 2.5,
width: MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width * 5)/100,
child: Center(
child: Text(
widget.userEmail,
textAlign: TextAlign.center,
),
),
);
},
),
),
);
}
Widget _buildPage()
{
return Column(children: <Widget>[
_buildBox(),
_buildList(),
],);
}
Widget _buildList()
{
return ListView(
children: <Widget>[
ListTile(
title: Text('Sun'),
),
ListTile(
title: Text('Moon'),
),
ListTile(
title: Text('Star'),
),
],
);
}
}
I just modified your code with Scaffold put the top Widget before the SafeArea, Please check the below code of it.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _HomeScreen();
}
}
class _HomeScreen extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
return SafeArea(
top: true,
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
margin: const EdgeInsets.only(top: 20.0),
decoration: BoxDecoration(
color: Color(0xFF8185E2),
border: Border.all(
color: Color(0xFF8185E2),
),
borderRadius: BorderRadius.all(Radius.circular(20))),
height: MediaQuery.of(context).size.height / 2.5,
width: MediaQuery.of(context).size.width -
(MediaQuery.of(context).size.width * 5) / 100,
child: Center(
child: Text(
"YOUR_EMAIL",
textAlign: TextAlign.center,
),
),
),
Expanded(
child: _buildList(),
)
],
),
),
);
Column(
children: <Widget>[
// _buildBox(),
_buildList(),
],
);
}
Widget _buildList() {
return ListView(
children: <Widget>[
ListTile(
title: Text('Sun'),
),
ListTile(
title: Text('Moon'),
),
ListTile(
title: Text('Star'),
),
],
);
}
}
And output of the program as follow
Scaffold Widget should be a top Widget that contains the Column widget and all children Widget. I think you can start learning how to layout Widget in Flutter in order to understand more the way how Widget works, and the good place can be: https://flutter.dev/docs/development/ui/layout#lay-out-a-widget
Coming back to your question, you can just fix a bit to make it work:
class _ProfileBoxState extends State<ProfileBox> {
#override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildBox() {
return Align(
alignment: Alignment.topCenter,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
margin: const EdgeInsets.only(top: 20.0),
decoration: BoxDecoration(
color: Color(0xFF8185E2),
border: Border.all(
color: Color(0xFF8185E2),
),
borderRadius: BorderRadius.all(Radius.circular(20))),
height: constraints.maxHeight / 2.5,
width: MediaQuery.of(context).size.width -
(MediaQuery.of(context).size.width * 5) / 100,
child: Center(
child: Text(
widget.userEmail,
textAlign: TextAlign.center,
),
),
);
},
),
);
}
Widget _buildPage() {
return Scaffold(
body: Column(
children: <Widget>[
_buildBox(),
_buildList(),
],
),
);
}
Widget _buildList() {
return ListView(
children: <Widget>[
ListTile(
title: Text('Sun'),
),
ListTile(
title: Text('Moon'),
),
ListTile(
title: Text('Star'),
),
],
);
}
}