Accessing to a class property outside of the class - dart

What are the ways to change a property of a class from outside of the class in flutter?
This is a code for BackDrop layout written in dart for Flutter and I want to close the front panel from the class FrontPanel(and that property is for another class, as you see BackPanel class).
I have tried GlobalKey and Setter method to change that property but I couldn't.
How Can I do that?
Regards.
import 'package:flutter/material.dart';
import 'backdrop.dart';
class SimpleExample extends StatelessWidget {
#override
Widget build(BuildContext context) =>
Scaffold(body: SafeArea(child: Panels()));
}
class Panels extends StatelessWidget {
final frontPanelVisible = ValueNotifier<bool>(false);
#override
Widget build(BuildContext context) {
return Backdrop(
frontLayer: FrontPanel(),
backLayer: BackPanel(
frontPanelOpen: frontPanelVisible,
),
frontHeader: FrontPanelTitle(),
panelVisible: frontPanelVisible,
frontPanelOpenHeight: 40.0,
frontHeaderHeight: 48.0,
frontHeaderVisibleClosed: true,
);
}
}
class FrontPanelTitle extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 16.0, left: 16.0),
child: Text(
'Tap Me',
style: Theme.of(context).textTheme.subhead,
),
);
}
}
class FrontPanel extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).cardColor,
child: Center(child: Text('Hello world')));
}
}
class BackPanel extends StatefulWidget {
BackPanel({#required this.frontPanelOpen});
final ValueNotifier<bool> frontPanelOpen;
#override
createState() => _BackPanelState();
}
class _BackPanelState extends State<BackPanel> {
bool panelOpen;
#override
initState() {
super.initState();
panelOpen = widget.frontPanelOpen.value;
widget.frontPanelOpen.addListener(_subscribeToValueNotifier);
}
void _subscribeToValueNotifier() =>
setState(() => panelOpen = widget.frontPanelOpen.value);
/// Required for resubscribing when hot reload occurs
#override
void didUpdateWidget(BackPanel oldWidget) {
super.didUpdateWidget(oldWidget);
oldWidget.frontPanelOpen.removeListener(_subscribeToValueNotifier);
widget.frontPanelOpen.addListener(_subscribeToValueNotifier);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Center(
child: Padding(
padding: const EdgeInsets.only(
top: 10.0,
),
child: Text('Front panel is ${panelOpen ? "open" : "closed"}'),
)),
Center(
child: RaisedButton(
child: Text('Tap Me'),
onPressed: () {
widget.frontPanelOpen.value = true;
},
)),
// will not be seen; covered by front panel
Center(child: Text('Bottom of Panel')),
]);
}
}

Actually found the answer .
You would be able to do that by making an animation controller and pass the controller to the FrontPages statefulwidget and then using that in the StateClass by widget..(value: newValue)

Related

Flutter - My animation is only playing once

I wanted to animate text and I put a nice animation. So I've made a statefull widget called AnimatedText() and I use it in an other stateful widget that use this AnimatedText and refresh the text every time you clic the screen. However, when I change this text with a setState(), the animation doesn't play again if it had already played...
Here is my code, the AnimatedText() :
class AnimatedText extends StatefulWidget {
double textSize;
String text;
AnimatedText(this.text, {this.textSize = 20});
#override
_AnimatedTextState createState() => _AnimatedTextState();
}
class _AnimatedTextState extends State<AnimatedText> with SingleTickerProviderStateMixin {
Animation _fontSizeAnimation;
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(duration: Duration(milliseconds: 250), vsync: this);
_fontSizeAnimation = CurvedAnimation(parent: _controller, curve: Curves.bounceOut);
_fontSizeAnimation.addListener(() => setState(() {}));
_controller.forward();
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Text(widget.text, textAlign: TextAlign.center, style: GoogleFonts.rubik(color: Colors.white, fontSize: _fontSizeAnimation.value*ResponsiveSize().responsiveSizeSmall(widget.textSize)));
}
}
And here is the class that use the AnimatedText():
class BasicDisplay extends StatefulWidget {
Question question;
VoidCallback onTap;
VoidCallback onPressed;
BasicDisplay(this.question, this.onTap, this.onPressed);
#override
_BasicDisplayState createState() => _BasicDisplayState();
}
class _BasicDisplayState extends State<BasicDisplay> with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return GamePageBackground(
onTap: widget.onTap,
question: widget.question,
children: <Widget>[
Expanded(
child: Container(
alignment: Alignment.topRight,
child: IconButton(
onPressed: widget.onPressed,
icon: Icon(Icons.clear, color: Color(0xaaffffff), size: ResponsiveSize().responsiveSize(quitIconButtonSize),),
),
),
),
AnimatedText(widget.question.category, textSize: questionCategoryTextSize,),
AnimatedText(widget.question.question, textSize: questionTextSize,),
Expanded(
child: Container(
padding: EdgeInsets.only(bottom: ResponsiveSize().responsiveSize(10)),
alignment: Alignment.bottomCenter,
child: Text(widget.question.explanation, textAlign: TextAlign.center ,style: GoogleFonts.rubik(color: Colors.white, fontSize: ResponsiveSize().responsiveSize(bottomExplanationTextSize))),
),
),
],
);
}
}
The onPressed call back call the Game class that will refresh the text.
Any one know how to solve this problem ?
I found a solution based on #HBS idea !
I took his function and changed it a little bit :
#override
void didUpdateWidget(AnimatedText oldWidget) {
if(oldWidget.text != widget.text) {
_controller.reset();
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}
You can override the didUpdateWidget in your AnimatedText class:
#override
void didUpdateWidget(AnimatedText oldWidget) {
if(oldWidget.text != widget.text) {
_controller.reset();
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}

From child set state of other child component

Let's say we have class Car (Stateless) that have inside it two class for now it will be Wheel (Statefull) and Mask (Statefull) and my job is to whenever state of class Wheel is changed call class Mask to change it state also with specific data from Wheel, but parent should have also access to child data. How can I achieve it?
import 'package:flutter/material.dart';
void main() {
runApp(Car());
}
class Car extends StatelessWidget {
int childMaskVal = ..??????
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(
title: Text('App bar'),
),
body: Column(
children: <Widget>[
Wheel(),
Mask(),
],
),
),
);
}
}
class Wheel extends StatefulWidget {
_WheelState createState() => _WheelState();
}
class _WheelState extends State<Wheel> {
int _value = 0;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
_value++;
});
},
),
Text(_value.toString()),
],
),
);
}
}
class Mask extends StatefulWidget {
_MaskState createState() => _MaskState();
}
class _MaskState extends State<Mask> {
int _value = 13;
#override
Widget build(BuildContext context) {
return Container(
child: Text((_value * Wheel._value).toString()),???????
);
}
}
You can pass the callback to the child.
In the example you provided, the parent should be a statefulWidget and the children should be a stateless widget. You should initialize the state in the parent and then pass it to the child.
If you find that the parent needs to access the data in the child, that means you need to move the state up.
See the example below:
import 'package:flutter/material.dart';
void main() {
runApp(Car());
}
class Car extends StatefulWidget{
#override
_CarState createState() => _CarState();
}
class _CarState extends State<Car> {
int childMaskVal = ..??????
int _value = 1;
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(
title: Text('App bar'),
),
body: Column(
children: <Widget>[
Wheel(
value: _value,
onPressed: () {
setState(() {
_value++;
// update state here
})
}),
Mask(),
],
),
),
);
}
}
class Wheel extends Stateless {
int value;
Function onPressed;
Wheel({this.value, this.onPresed})
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
onPressed: onPressed
),
Text(value.toString()),
],
),
);
}
}
You can also check this post How to pass data from child widget to its parent

How to fade-in a new screen from the bottom following the direction of a vertical drag action?

I need to navigate to a new screen with a fade-in animation from the bottom of the screen, following the direction of vertical drag on a custom material widget.
I have created two screens, Screen -1 and screen -2. On screen - 1, I have a Container widget. I have wrapped the widget inside a GestureDetector and I am trying Navigate to screen - 2 on vertical drag, which I am using GestureDetector.onVerticalDrag property.
import 'package:flutter/material.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/':(context) => ScreenOne(),
'/two': (context) => ScreenTwo(),
},
title: 'Screens',
);
}
}
class ScreenOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-1'),
),
body: GestureDetector(
onVerticalDragStart: (DragStartDetails details){
Navigator.pushNamed(context, '/two');
},
child: Container(
margin: EdgeInsets.all(8.0),
color: Colors.red,
),
),
);
}
}
class ScreenTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-2'),
),
body: Center(
child: Text('Screen-2'),
),
);
}
}
Can you please help me get the expected transition. I am attaching a GIF for reference.
If you don't mind using PageView, this is much easier with it and Opacity widget. Here is the demo video.
import 'package:flutter/material.dart';
class TestView extends StatefulWidget {
#override
_TestViewState createState() => _TestViewState();
}
class _TestViewState extends State<TestView> {
PageController pageController;
double panPosition = 1; // dummy value prevents division with 0
double deviceHeight;
void updatePageState() {
setState(() {
panPosition =
pageController.position.pixels.abs(); // updates pan position
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
pageController = PageController(
keepPage: true,
);
pageController
.addListener(updatePageState); // add listener to page controller.
}
#override
Widget build(BuildContext context) {
deviceHeight =
MediaQuery.of(context).size.height; //get device screen height
return Scaffold(
backgroundColor: Colors.white,
body: PageView(
controller: pageController,
scrollDirection: Axis.vertical, //vertical scroll
children: <Widget>[
Opacity(
// opacity handles the transition effect
opacity: 1 - (panPosition / deviceHeight),
//first screen opacity goes from 1 to 0
child: ScreenOne(),
),
Opacity(
opacity: (panPosition / deviceHeight),
//first screen opacity goes from 0 to 1
child: ScreenTwo(
title: "this title is from parent widget.",
),
)
],
),
);
}
}
class ScreenOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-1'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 96,
color: Colors.red,
)
],
),
);
}
}
class ScreenTwo extends StatelessWidget {
final String title;
const ScreenTwo({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(title),
),
);
}
}

How to implement the AccountDetail of the UserAccountsDrawerHeader widget to be displayed the same as the Gmail App with Flutter?

I'm using the Flutter UserAccountsDrawerHeader widget to display the user's data but I could not figure out how to implement the onDetailsPressed() function to call the user details. Here is my code:
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return new Scaffold(
drawer: _buildDrawer(context),
appBar: _buildAppBar(),
);
}
}
Widget _buildAppBar() {
return new AppBar();
}
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new ListView(
children: <Widget>[
new UserAccountsDrawerHeader(
accountName: new Text("Cleudice Santos"),
accountEmail: new Text("cleudice.ms#gmail.com"),
onDetailsPressed: () {},
),
new ListTile(
title: new Text("Visão geral"),
leading: new Icon(Icons.dashboard),
onTap: () {
print("Visão geral");
},
),
],
),
);
}
I want to click the arrow and show the account details as shown below. That is, overlapping the content of the drawer. As the Gmail app does.
Basically, what you should be doing is replacing the rest of the content with user details rather than the current list. The simplest way to do this is to make your drawer into a stateful widget and have a boolean that keeps track of whether user details or the normal list should be shown.
I've added that to your code (and added a bit to make it self-contained so you can paste it to a new file to test out):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: UserDetailDrawer(),
appBar: _buildAppBar(),
);
}
}
Widget _buildAppBar() {
return AppBar();
}
class UserDetailDrawer extends StatefulWidget {
#override
_UserDetailDrawerState createState() => _UserDetailDrawerState();
}
class _UserDetailDrawerState extends State<UserDetailDrawer> {
bool showUserDetails = false;
Widget _buildDrawerList() {
return ListView(
children: <Widget>[
ListTile(
title: Text("Visão geral"),
leading: Icon(Icons.dashboard),
onTap: () {
print("Visão geral");
},
),
ListTile(
title: Text("Another tile??"),
leading: Icon(Icons.question_answer),
),
],
);
}
Widget _buildUserDetail() {
return Container(
color: Colors.lightBlue,
child: ListView(
children: [
ListTile(
title: Text("User details"),
leading: Icon(Icons.info_outline),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(children: [
UserAccountsDrawerHeader(
accountName: Text("Cleudice Santos"),
accountEmail: Text("cleudice.ms#gmail.com"),
onDetailsPressed: () {
setState(() {
showUserDetails = !showUserDetails;
});
},
),
Expanded(child: showUserDetails ? _buildUserDetail() : _buildDrawerList())
]),
);
}
}

Emit the data to parent Widget in Flutter

I'm trying to set the text from child widget to parent widget. But the text is not reflecting in parent widget.
Tried to use setState() also but still unable to get expected result.
Following is my code:
void main() => runApp(new TestApp());
class TestApp extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<TestApp>{
String abc = "";
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new Column(
children: <Widget>[
new Text("This is text $abc"),
TestApp2(abc)
],
),
),
);
}
}
class TestApp2 extends StatefulWidget {
String abc;
TestApp2(this.abc);
#override
_TestState2 createState() => new _TestState2();
}
class _TestState2 extends State<TestApp2>{
#override
Widget build(BuildContext context) {
return new Container(
width: 150.0,
height: 30.0,
margin: EdgeInsets.only(top: 50.0),
child: new FlatButton(
onPressed: (){
setState(() {
widget.abc = "RANDON TEXT";
});
},
child: new Text("BUTTON"),
color: Colors.red,
),
);
}
}
Am i missing something ?
In your example, a few assumptions were made. I will try to remove one by one.
You pass abc from parent to child and you mutated the child value on press on button. As primitive types are pass by value in dart, change in the value of abc in child will not change the value of parent abc. Refer the below snippet.
void main() {
String abc = "oldValue";
changeIt(abc);
print(abc); // oldValue
}
void changeIt(String abc) {
abc = "newValue";
print(abc); //newValue
}
Let's assume the first one is wrong(for understanding purpose). Then changing the value of abc in child will change the value of abc in parent. But without calling that inside setState of parent, parent will not reflect the change. In your case if you change the code as below, it will change the button text alone on click (as setState of child is called).
new FlatButton(
onPressed: () {
setState(
() {
widget.abc = "RANDON TEXT";
},
);
},
child:
new Text(widget.abc), // setting the text based on abc
color: Colors.red,
),
Instead of using globalState which will be very difficult to maintain/debug as app grows, I would recommend using callbacks. Please refer the below code.
void main() => runApp(new TestApp());
class TestApp extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<TestApp> {
String abc = "bb";
callback(newAbc) {
setState(() {
abc = newAbc;
});
}
#override
Widget build(BuildContext context) {
var column = new Column(
children: <Widget>[
new Text("This is text $abc"),
TestApp2(abc, callback)
],
);
return new MaterialApp(
home: new Scaffold(
body: new Padding(padding: EdgeInsets.all(30.0), child: column),
),
);
}
}
class TestApp2 extends StatefulWidget {
String abc;
Function(String) callback;
TestApp2(this.abc, this.callback);
#override
_TestState2 createState() => new _TestState2();
}
class _TestState2 extends State<TestApp2> {
#override
Widget build(BuildContext context) {
return new Container(
width: 150.0,
height: 30.0,
margin: EdgeInsets.only(top: 50.0),
child: new FlatButton(
onPressed: () {
widget.callback("RANDON TEXT"); //call to parent
},
child: new Text(widget.abc),
color: Colors.red,
),
);
}
}
To write the very precise answer. Just use the call back like the above answer use this.
So you want to call the state of ParentScreen from the another function/widget/class. Just follow this code
import 'package:showErrorMessage.dart';
class ParentScreen extends StatefulWidget {
ParentScreen({Key key}) : super(key: key);
#override
_ParentScreenState createState() => _ParentScreenState();
}
class _ParentScreenState extends State<ParentScreen> {
callback() {
setState(() {});
}
#override
Widget build(BuildContext context) {
String message = "hello";
return Container(
child: showErrorMessage(message, callback);,
);
}
}
And here is the child widget/function/class
import 'package:flutter/material.dart';
showErrorMessage(message, Function callback) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
message,
style: TextStyle(color: Colors.white, fontSize: 16),
),
GestureDetector(
onTap: () {
callback(); // ------ this will change/rebuild the state of its parent class
},
child: Icon(
Icons.refresh,
size: 30,
color: Colors.white,
)),
],
));
}
The point that you are missing is your setState method call. You call the setState of the TestState2.
For fixing that, there are two ways.
First way is to create a GlobalKey(https://docs.flutter.io/flutter/widgets/GlobalKey-class.html) and pass it as a parameter to the child widget.
And the second way is to create a global variable for the parent state and use it in the child state.
I modified the code below with the second approach.
_TestState _globalState = new _TestState();
class TestApp extends StatefulWidget {
#override
_TestState createState() => _globalState;
}
class _TestState extends State<TestApp>{
String abc = "";
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new Column(
children: <Widget>[
new Text("This is text $abc"),
TestApp2()
],
),
),
);
}
}
class TestApp2 extends StatefulWidget {
TestApp2();
#override
_TestState2 createState() => new _TestState2();
}
class _TestState2 extends State<TestApp2>{
#override
Widget build(BuildContext context) {
return new Container(
width: 150.0,
height: 30.0,
margin: EdgeInsets.only(top: 50.0),
child: new FlatButton(
onPressed: (){
_globalState.setState((){
_globalState.abc = "Button clicked";
});
},
child: new Text("BUTTON"),
color: Colors.red,
),
);
}
}

Resources