I have a problem. I have an InkWell widget but I don't want it to have that splash screen animation. Is there a way to get rid of this animation?
If you don't care about the splash animation, do not use InkWell. That widget exists only for that.
Instead use GestureDetector, which is basically an InkWell without the animation.
If you do not want any feedback you could add splashColor: Colors.transparent and highlightColor: Colors.transparent
InkWell Widget has a SplashColor property
class Whatever extends StatelessWidget {
#override
Widget build(BuildContext context) {
// The InkWell Wraps our custom flat button Widget
return InkWell(
// When the user taps the button, show a snackbar
onTap: () { /*or your custom implementation*/
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Tap'),
));
},
splashColor: Colors.transparent, /*This is what you need to include*/
child: Container(
padding: EdgeInsets.all(12.0),
child: Text('Flat Button'),
),
);
}
}
Related
I'm trying to make a calculator app using flutter where instead of taking input through the keyboard I want to take input through some buttons. The issue comes when I press a button but it does not display the corresponding data in the Text widget above.
All my classes are stateless except for the first MyApp class, which is Stateful.
I tried by creating a general variable outside all the classes and using that to transfer text from the button class to the display class but that did not work.
The general variable is "_calcText"
class DisplayAnswer extends StatelessWidget {
final String _text;
DisplayAnswer(this._text);
#override
Widget build(BuildContext context) {
return Expanded(
flex: 2,
child: Material(
color: Colors.greenAccent,
child: Ink(
padding: EdgeInsets.all(10.0),
child: Center(
child: Container(
constraints: BoxConstraints.expand(),
decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 5.0), color: Colors.white),
child: Text(_text,style: TextStyle(fontSize: 50.0), textAlign: TextAlign.center,),
),
),
),
),
);
}
}
class NumButtons extends StatelessWidget {
final String _number;
NumButtons(this._number);
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () {
_calcText = _calcText + _number;
print(_calcText);
DisplayAnswer(_calcText);
} ,
child: Text(_number.toString(), style: TextStyle(fontSize: 30.0),),
color: Colors.white
);
}
}
I want to display the value of _calcText in the Text widget of DisplayAnswer. I want _calcText to also change as other buttons are clicked, ie; if 2 is clicked Text should only display 2, if 5 is clicked after that it should display 25
The full code is here:
https://drive.google.com/open?id=1C4MLAkjowloicbjBP_uV8BfpPzhz4Yxf
Use Statefull widget insted of StatelessWidget.
Call the setState() method on onPressed function, after adition operation. It will build your widget with a new value.
On DisplayAnswer, you have to make a function to increment the value, than pass this function as parameter to NumButtons.
Pass a callback Function to NumButtons, like:
class NumButtons extends StatelessWidget {
final String _number;
final Function callback;
...
For color, I can use dialogBackgroundColor property to give AlertDialog background my own color.
I was looking to use Gradient as my background. How can I use that? DecoratedBox is something that will be needed, but I don't know what to wrap in what. Can anyone give me idea or link for the same?
You can add a Container inside which will be decorated with gradient. For example:
class GradientDialog extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new _GradientDialogState();
}
}
class _GradientDialogState extends State<GradientDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Container(
padding: const EdgeInsets.all(8.0),
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: AppColors.BG_GRADIENT,
begin: Alignment.topCenter,
end: Alignment.bottomCenter)),
child: YourContentInside(),
),
contentPadding: EdgeInsets.all(0.0),
);
}
}
Open it with
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return GradientDialog();
});
In build method of AlertDialog there is return Dialog(child: dialogChild, shape: shape);. In Dialog.build() - it returns Material(color: _getColor(context), .... There is no way to set gradient background for AlertDialog without customization.
I can add example if it'll be needed.
P.S. Or you can call showDialog and send another widget instead of AlertDialog.
I have a TextFormField that reloads the current screen when I tap on it to enter text. When I tap on the formfield the software keyboard is displayed briefly before the entire screen reloads and renders all the widgets again. I am running the app on an Android device.
Container(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Your input cannot be empty';
}
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
print('validated');
}
},
child: Text('Save'),
),
),
],
),
),
margin: EdgeInsets.only(top:8.0),
),
The problem is that the controller of the TextFormField is rebuild when you click on the field, and that's the reason of your issue.
So to solve that, did you try to create a Statefull widget and then creating a TextEditingController in the State of this widget and passing it as an argument to the TextFormField ?
I had the same Problem. this was my code
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Model model = Model();
#override
Widget build(BuildContext context) {
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
var mediaWidth = MediaQuery.of(context).size.width / 2.0;
return Scaffold(
...
and I solved this problem by declaring the _formKey outside of build method. and this worked for me.
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Model model = Model();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
var mediaWidth = MediaQuery.of(context).size.width / 2.0;
return Scaffold(
...
hope it will help you
Yes, that happens because when the keyboard appears, the flutter scaffold gets resize to the current available screen size. So, we can easily handle this by preventing the scaffold size change. I suggest to set scaffold resizeToAvoidBottomInset property false. If it's true the body and the scaffolds floating widgets should size themselves to avoid the onscreen keyboard whose height is defined by the ambient MediaQuery's, MediaQueryData,viewInsets bottom property.
Solution:
resizeToAvoidBottomInset: false,
Complete example:
#override
Widget build(BuildContext context) {
setDisplayData();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: getAppBar(),
body: OrientationBuilder(
builder: (context, orientation) {
return orientation == Orientation.portrait
? _buildVerticalLayout()
: _buildHorizontalLayout();
},
),
);
Check if you are using MediaQueries wrongly in your project, I had similar issue and it stopped when I changed the MediaQuery
in my case:
Size _size = MediaQuery.of(context).size;
removing this piece of code fixed my app.
When TextFormField focused the size of screen will changed because of the appearance of keyboard, that cause rebuild of state, you cant prevent re-build of state.
Instead of trying prevent re-build state, you need to solve problems which happen when state do re-build, one of common problem is declaration and initialization variables inside build(BuildContext context){ ... }' function.
The main problem, when you need to get some data related of context (like size of screen), in this case I prefer to pass this value from parent Widget...
For example this code will cause problem when re-build state:
#override
Widget build(BuildContext context) {
double? _screenHeight = MediaQuery.of(context).size.height;
return Container();
}
To solve problem get _screenHeight from parent, to know how to do that look at https://stackoverflow.com/a/50289032/2877427
I am trying to change the color of the element the user clicked on using a GestureDetector:
new GestureDetector(
onTap: (){
// Change the color of the container beneath
},
child: new Container(
width: 80.0,
height: 80.0,
margin: new EdgeInsets.all(10.0),
color: Colors.orange,
),
),
The problem is that I can't use setState inside of onTap. Otherwise I would have created a color variable. Any suggestions?
You can use setState() inside of onTap. In fact, that's exactly the right thing to do in this situation. If you are having trouble calling setState(), make sure your widget is stateful (see the interactivity tutorial).
You might also want to check out FlatButton or InkWell as more material-y ways to capture touches. If you really want a GestureDetector, read up on HitTestBehavior to make sure you're configuring it correctly.
Here's an example that changes to a random color every time it's clicked.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHome(),
);
}
}
class MyHome extends StatefulWidget {
#override
State createState() => new _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
final Random _random = new Random();
Color _color = Colors.orange;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new GestureDetector(
onTap: () {
// Change the color of the container beneath
setState(() {
_color = new Color.fromRGBO(
_random.nextInt(256),
_random.nextInt(256),
_random.nextInt(256),
1.0
);
});
},
child: new Container(
width: 80.0,
height: 80.0,
margin: new EdgeInsets.all(10.0),
color: _color,
),
),
),
);
}
}
Our app is built on top of Scaffold and to this point we have been able to accommodate most of our routing and navigation requirements using the provided calls within NavigatorState (pushNamed(), pushReplacementNamed(), etc.). What we don't want though, is to have any kind of 'push' animation when a user selects an item from our drawer (nav) menu. We want the destination screen from a nav menu click to effectively become the new initial route of the stack. For the moment we are using pushReplacementNamed() for this to ensure no back arrow in the app bar. But, the slide-in-from-the-right animation implies a stack is building.
What is our best option for changing that initial route without animation, and, can we do that while also concurrently animating the drawer closed? Or are we looking at a situation here where we need to move away from Navigator over to just using a single Scaffold and updating the 'body' directly when the user wants to change screens?
We note there is a replace() call on NavigatorState which we assume might be the right place to start looking, but it's unclear how to access our various routes originally set up in new MaterialApp(). Something like replaceNamed() might be in order ;-)
What you're doing sounds somewhat like a BottomNavigationBar, so you might want to consider one of those instead of a Drawer.
However, having a single Scaffold and updating the body when the user taps a drawer item is a totally reasonable approach. You might consider a FadeTransition to change from one body to another.
Or, if you like using Navigator but don't want the default slide animation, you can customize (or disable) the animation by extending MaterialPageRoute. Here's an example of that:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
// Fades between routes. (If you don't want any animation,
// just return child.)
return new FadeTransition(opacity: animation, child: child);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Navigation example',
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/': return new MyCustomRoute(
builder: (_) => new MyHomePage(),
settings: settings,
);
case '/somewhere': return new MyCustomRoute(
builder: (_) => new Somewhere(),
settings: settings,
);
}
assert(false);
}
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Navigation example'),
),
drawer: new Drawer(
child: new ListView(
children: <Widget> [
new DrawerHeader(
child: new Container(
child: const Text('This is a header'),
),
),
new ListTile(
leading: const Icon(Icons.navigate_next),
title: const Text('Navigate somewhere'),
onTap: () {
Navigator.pushNamed(context, '/somewhere');
},
),
],
),
),
body: new Center(
child: new Text(
'This is a home page.',
),
),
);
}
}
class Somewhere extends StatelessWidget {
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Text(
'Congrats, you did it.',
),
),
appBar: new AppBar(
title: new Text('Somewhere'),
),
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new DrawerHeader(
child: new Container(
child: const Text('This is a header'),
),
),
],
),
),
);
}
}
Use PageRouteBuilder like:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (_, __, ___) => Screen2(),
transitionDuration: Duration.zero,
),
);
And if you want transition, simply add following property to above PageRouteBuilder, and change seconds to say 1.
transitionsBuilder: (_, a, __, c) => FadeTransition(opacity: a, child: c),