I have a login-page where I want the transition of the logo from large to small (and back) to be smoother. As it is now, it just pops between the two.
When the keyboard opens, I would like the logo to animate to the smaller position, instead of just instantly changing.
Does anyone know a way to do this?
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: AppColors.yellowLight,
// appBar: MainAppBar(),
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(vertical: 90),
child: Image(
image: ExactAssetImage('assets/icons/app_logo.png'),
height: MediaQuery.of(context).viewInsets.bottom == 0
? size.height * 0.3
: size.height * 0.15,
fit: BoxFit.fitHeight,
alignment: Alignment.center,
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 50),
child: MainTextField(
controller: emailController,
labelText: 'E-post',
hintText: 'Skriv e-post som abc#epost.no'),
),
Padding(
padding: const EdgeInsets.only(
left: 50.0, right: 50.0, top: 30, bottom: 30),
child: MainTextField(
controller: passwordController,
obscureText: true,
labelText: 'Passord',
hintText: 'Skriv inn sikkert passord',
),
),
LoginButton(
text: 'Logg inn',
isLoading: isLoading,
onPressed: _login,
),
// Text('New User? Create Account')
],
),
),
),
);
}
AnimatedContainer(
height: MediaQuery.of(context).viewInsets.bottom != 0 ? 100.0 : 200.0,
width: MediaQuery.of(context).viewInsets.bottom != 0 ? 100 : 200.0,
duration:const Duration(milliseconds: 750),
curve: Curves.fastOutSlowIn,
child: Image.network("https://picsum.photos/200/300"),
),
but MediaQuery.of(context).viewInsets.bottom it won't give you accuracy as if it's real time when changing the keyboard state, so I advice you to use another method to know the current state of the keyboard like the code below
you can use this package to listen for keyboard change flutter_keyboard_visibility here is the code
class _MyHomePageState extends State<MyHomePage> {
late StreamSubscription<bool> keyboardSubscription;
bool _isVisible = false;
#override
void initState() {
super.initState();
var keyboardVisibilityController = KeyboardVisibilityController();
// Subscribe
keyboardSubscription = keyboardVisibilityController.onChange.listen((bool visible) {
if(mounted) {
setState(() {
_isVisible = visible;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
height: _isVisible ? 100.0 : 200.0,
width: _isVisible ? 100 : 200.0,
duration:const Duration(milliseconds: 750),
curve: Curves.fastOutSlowIn,
child: Image.network("https://picsum.photos/200/300"),
),
Spacer(),
const Text(
'You have pushed the button this many times:',
),
TextFormField(
),
Spacer(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
#override
void dispose() {
keyboardSubscription.cancel();
super.dispose();
}
}
if you don't want to use package for keyboard visibility so you can use this code
class _MyHomePageState extends State<MyHomePage> {
final FocusNode _myNode = FocusNode();
bool _isVisible = false;
#override
void initState() {
super.initState();
_myNode.addListener(_listener);
}
void _listener(){
if(_myNode.hasFocus){
// keyboard appeared
if(mounted){
setState(() {
_isVisible = true;
});
}
}else{
// keyboard dismissed
if(mounted){
setState(() {
_isVisible = false;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedContainer(
height: _isVisible ? 100.0 : 200.0,
width: _isVisible ? 100 : 200.0,
duration:const Duration(milliseconds: 750),
curve: Curves.fastOutSlowIn,
child: Image.network("https://picsum.photos/200/300"),
),
Spacer(),
const Text(
'You have pushed the button this many times:',
),
TextFormField(
focusNode: _myNode,
),
Spacer(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
#override
void dispose() {
_myNode.removeListener(_listener);
super.dispose();
}
}
I found a way that kind of works. At least the animation does. But as it is based on MediaQuery.of(context).viewInsets.bottom == 0 still, the animation does not start until the keyboard is all the way out of view.
AnimatedContainer(
duration: Duration(milliseconds: 400),
curve: Curves.fastOutSlowIn,
padding: MediaQuery.of(context).viewInsets.bottom == 0
? EdgeInsets.symmetric(vertical: 100)
: EdgeInsets.only(top: 75, bottom: 10),
height: MediaQuery.of(context).viewInsets.bottom == 0
? _height.height * 0.5
: _height.height * 0.25,
child: Image(
image: ExactAssetImage('assets/icons/app_logo.png'),
),
),
Related
I have built an app using Flutter. I want to create a gender selection page for it, currently I have a plain drop-down type button. I want to have an effect like this but I have no idea how to get this can anyone help?
GIF of Sample
#override
initState() {
pageController;
super.initState();
}
PageController? pageController =
PageController(initialPage: 1, viewportFraction: .5);
#override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
TabController tabController = TabController(length: 2, vsync:this);
List genders = [
Image.asset("assets/fitness_app/area1.png"),
Image.asset("assets/fitness_app/area3.png")];
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
premiumDialogue();
},
backgroundColor: primary,
child: const Icon(
Icons.add,
),
),
backgroundColor: bgcolor,
appBar: AppBar(
toolbarHeight: 70,
leadingWidth: 65,
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text(
"Home Workouts",
style: h2black,
),
elevation: 0,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: PageView.builder(
controller: pageController,
clipBehavior: Clip.antiAlias,
physics: const BouncingScrollPhysics(),
itemCount: genders.length,
itemBuilder: (context, index) {
return genders[index];
},
),
),
),
],
);
I have implemented this code but I was able to achieve this only.
What I achieved
There are many approaches to achieving this you can your the pageview inside page view you can use the AnimatedContainer widget to animate the change in size and color of the selected image.
bool _isMaleSelected = false;
bool _isFemaleSelected = false;
void _selectMale() {
setState(() {
_isMaleSelected = true;
_isFemaleSelected = false;
});
}
void _selectFemale() {
setState(() {
_isMaleSelected = false;
_isFemaleSelected = true;
});
}
return Scaffold(
body: Container(
padding: padding as per your requirement,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
duration: Duration(milliseconds: as per your requirement),
height: _isMaleSelected ? height: height,
width: _isMaleSelected ? width: width:
decoration: BoxDecoration(
color: _isMaleSelected ? Colors as per your requirement: Colors as per your requirement,
and decoration as per your need
),
child: InkWell(
onTap: _selectMale,
child: Image.asset(
"your image path",
height: height as per your requirement,
width: width as per your requirement,
),
),
),
SizedBox(
height: height as per your requirement,
),
AnimatedContainer(
duration: Duration(milliseconds: as per your requirement),
height: _isFemaleSelected ? height as per your requirement: height as per your requirement,
width: _isFemaleSelected ? width as per your requirement: width as per your requirement,
decoration: BoxDecoration(
color: _isFemaleSelected ? Colors as per your requirement: Colors as per your requirement,
decoration as per your need
),
child: InkWell(
onTap: _selectFemale,
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 currently have a card widget with two animated containers of different colors. When the user clicks on either color, the other side of the container changes to match the users selection.
When the user clicks on a side, I want the color to expand out and fill about 90% of the card still showing just a sliver of the previous color. So for example if the card started out with red on the left and blue on the right, after clicking on the blue side, the blue would expand from right to left covering 90% of the card and the red would cover the remaining 10% on the left.
I hope that made sense. I am new to flutter so still just learning.
class _PickState extends State<Pick> {
Color _colorA = Colors.red;
Color _colorB = Colors.blue;
#override
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: GestureDetector(
child: AnimatedContainer(
color: _colorA,
child: const Text("Option A"),
alignment: Alignment.centerLeft,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickA();
},
),
),
Expanded(
child: GestureDetector(
child: AnimatedContainer(
color: _colorB,
child: const Text("Option B"),
alignment: Alignment.centerRight,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickB();
},
))
],
),
);
}
void _clickA() {
setState(() {
_colorA = Colors.red;
_colorB = Colors.red;
});
}
void _clickB() {
setState(() {
_colorA = Colors.blue;
_colorB = Colors.blue;
});
}
}
class _PickState extends State<Pick> {
int _colorAPercentage = 90;
int _colorBPercentage = 10;
#override
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Expanded(
flex: _colorAPercentage,
child: GestureDetector(
child: AnimatedContainer(
color: Colors.red,
child: const Text("Option A"),
alignment: Alignment.centerLeft,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickA();
},
),
),
Expanded(
flex: _colorBPercentage,
child: GestureDetector(
child: AnimatedContainer(
color: Colors.blue,
child: const Text("Option B"),
alignment: Alignment.centerRight,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickB();
},
))
],
),
);
}
void _clickA() {
setState(() {
_colorAPercentage = 90;
_colorBPercentage = 10;
});
}
void _clickB() {
setState(() {
_colorAPercentage = 10;
_colorBPercentage = 90;
});
}
}
If I understood correctly, try this.
#override
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: GestureDetector(
child: AnimatedContainer(
color: Colors.red,
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickA();
},
),
),
Expanded(
flex: 4,
child: GestureDetector(
child: AnimatedContainer(
color: _colorA,
child: const Text("Option A"),
alignment: Alignment.centerLeft,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickA();
},
),
),
Expanded(
flex: 4,
child: GestureDetector(
child: AnimatedContainer(
color: _colorB,
child: const Text("Option B"),
alignment: Alignment.centerRight,
constraints: const BoxConstraints(minHeight: 75),
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickB();
},
)),
Expanded(
child: GestureDetector(
child: AnimatedContainer(
color: Colors.blue,
duration: const Duration(milliseconds: 250),
),
onTap: () {
_clickB();
},
))
],
),
);
}
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),
);
});
}
}
I'm using animated container for getting toggle button like in android. Here my code is working for toggle button function perfectly. But what i need is, if i press some other button, toggle button should function. see the below image for your reference(if i press "Press" button it should operate toogle button). Thanks in advance.
https://i.stack.imgur.com/qJpWT.jpg
class DialogExample extends StatefulWidget {
#override
_DialogExampleState createState() => new _DialogExampleState();
}
class _DialogExampleState extends State<DialogExample> {
bool toggleValue = false;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(top:180.0),
child: Center(
child: InkWell(
onTap: toggleButton,
child: AnimatedContainer(
duration: Duration(milliseconds: 100),
height: 40.0,
width: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: toggleValue ? Colors.deepOrangeAccent : Colors.grey.withOpacity(0.5),
),
child: Stack(
children: [
AnimatedPositioned(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn,
left: toggleValue? 60.0 : 0.0,
right: toggleValue ? 0.0 : 60.0,
child: InkWell(
onTap: toggleButton,
child: AnimatedSwitcher(
duration: Duration(milliseconds: 100),
child: toggleValue ? Icon(Icons.check_circle,color: Colors.white,size: 39.0, key: UniqueKey(),)
: Icon(Icons.check_circle,color: Colors.white,size: 39.0,
key: UniqueKey(),),
),
),
)
],
),
),
),
),
),
Padding(
padding: const EdgeInsets.only(top:80.0),
child: RaisedButton(
child: Text("press"),
onPressed: (){
toggleButton;
},
),
)
],
),
);
}
toggleButton() {
setState(() {
toggleValue = !toggleValue;
});
}
}
Replace your button code with the below code block. Actually you are calling toggle button inside a method, So, it is unable to recognition it.
Padding(
padding: const EdgeInsets.only(top:80.0),
child: RaisedButton(
child: Text("press"),
onPressed:toggleButton,
),
)
Just replace toggleButton with, because that is not valid statement here.
toggleButton();