I have the below code and I would like to add a floatingactionbutton to turn on and off the camera flash. I tried different ways but was not able to do it. Any suggestions would help. I tired different packages like torch, torch-light etc and it didn't work I think is because I have already called the camera package
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
class CameraScreen extends StatefulWidget {
CameraScreen({Key key, #required this.controller}) : super(key: key);
final CameraController controller;
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
#override
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size.width;
return Container(
child: ShaderMask(
shaderCallback: (rect) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Colors.black, Colors.transparent])
.createShader(Rect.fromLTRB(0, 0, rect.width, rect.height / 4));
},
blendMode: BlendMode.darken,
child: Transform.scale(
scale: 1.0,
child: AspectRatio(
aspectRatio: MediaQuery.of(context).size.aspectRatio,
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitHeight,
child: Container(
width: size,
height: size / widget.controller.value.aspectRatio,
child: Stack(
children: <Widget>[
CameraPreview(widget.controller),
],
),
),
),
),
),
),
)
);
}
#override
void dispose() {
super.dispose();
}
}
You can wrap your widget with scaffold and can add float button inside the scaffold like below :
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size.width;
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: (){},),
body: ShaderMask(
shaderCallback: (rect) {
return const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Colors.black, Colors.transparent])
.createShader(Rect.fr
///// your code ///////
Related
I follow the flutter documentation create a shimmer loading effect class like below
import 'package:flutter/widgets.dart';
const _shimmerGradient = LinearGradient(
colors: [
Color(0xFFEBEBF4),
Color(0xFFF00000),
Color(0xFFEBEBF4),
],
stops: [
0.1,
0.3,
0.4,
],
begin: Alignment(-1.0, -0.3),
end: Alignment(1.0, 0.3),
tileMode: TileMode.clamp,
);
class ShimmerLoading extends StatefulWidget {
const ShimmerLoading({
super.key,
required this.isLoading,
required this.child,
});
final bool isLoading;
final Widget child;
#override
State<ShimmerLoading> createState() => _ShimmerLoadingState();
}
class _ShimmerLoadingState extends State<ShimmerLoading> {
#override
Widget build(BuildContext context) {
if (!widget.isLoading) {
return widget.child;
}
return ShaderMask(
blendMode: BlendMode.srcATop,
shaderCallback: (bounds) {
return _shimmerGradient.createShader(bounds);
},
child: widget.child,
);
}
}
and i implement it on test screen.
test_screen.dart
class _TestScreenState extends BaseConsumerState<TestScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...
ShimmerLoading(
isLoading: true,
child: Container(
width: 200,
height: 50,
color: Colors.red,
),
),
...
}
}
I tested it on the emulator.
It works on Android but doesn't work on IOS.
How can I make sure that will work on the real device?
IOS' screen
the black square is a shimmer loading effect.
Try Shimmer Package
Package From here
So I have a Card with a column (dynamic size). I want to display an overlay (semi transparent + icon) over the whole card, without altering the card's size.
I have coded an example by using a Stack widget. Run on DartPad
The problem is, that the overlay should adapt to the size of the content in the layer below.
But how does the Overlay know the size of that other layer?
double.infinity won't work, because the size of the card is not fixed and can't be fixed, because the content has a dynamic size.
Card(
child: Stack(
children: [
// Content
Padding(
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
// Overlay
_showOverlay == true
? Container(
height: double.infinity, // Overlay is too big -> should adabt to
width: double.infinity, // the size of the layer underneath
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
),
)
: Container( // Overlay deactivated
height: 0,
width: 0,
),
],
),
color: Colors.blue,
))
You can copy paste run full code below
In this case, you can use GlobalKey to get yourRenderBox.size
code snippet
class _MyHomePageState extends State<MyHomePage> {
bool _showOverlay = false;
GlobalKey _key = GlobalKey();
Size _getSizes() {
final RenderBox yourRenderBox = _key.currentContext.findRenderObject();
return yourRenderBox.size;
}
#override
Widget build(BuildContext context) {
...
Padding(
key: _key,
...
// Overlay
_showOverlay == true
? Container(
height: _getSizes().height,
width: _getSizes().width,
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Problem'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showOverlay = false;
GlobalKey _key = GlobalKey();
Size _getSizes() {
final RenderBox yourRenderBox = _key.currentContext.findRenderObject();
return yourRenderBox.size;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Card(
child: Stack(
children: [
// Content
Padding(
key: _key,
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
// Overlay
_showOverlay == true
? Container(
height: _getSizes().height,
width: _getSizes().width,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
),
)
: Container(
// Overlay deactivated
height: 0,
width: 0,
),
],
),
color: Colors.blue,
)));
}
}
Make Container as a parent of your widget, modified code :
Container(
decoration: _showOverlay ? BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
)
: null,
child: Padding(
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min,
children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
),
I was having this same issue, and as of september 2022, #chunhunghan solution didn't work for me: final RenderBox yourRenderBox = _key.currentContext.findRenderObject(); is the line that breaks my code with: A value of type 'RenderObject?' can't be assigned to a variable of type 'RenderBox'.
So I came up with another approach, inspired by flutter's Widget of the Week video about Positioned, where no Keys are needed, simply a Stack() with two layers: bottom is dynamic-sized, and top is wrapped inside a Positioned.fill() widget that will adapt to the bottom layer size automatically.
Simplified code looks like this:
class MyStackState extends State < MyStack > {
bool _showOverlay = false;
#override
Widget build(BuildContext context) {
// we set up the Stack
return Stack(
children: [
// Content of dynamic size (bottom layer)
Padding(
// your dynamic content here...
),
// Overlay (top layer that adapts to the size of bottom layer)
if (_showOverlay == true)
Positioned.fill(
child: Container(
// your overlay here...
),
),
],
);
}
}
Note that the Positioned.fill() will strictly take the size of the underlying layer, so if the underlying layer is of height 0 or width 0, then nothing will be rendered, not even if the Positioned.fill() had sized content inside, for example in my case I needed a CircularProgressIndicator() there to let the user know something was processing... it didn't show if content was empty.
I edited the DartPad link you provided with a functioning example of this solution, hope it helps! Edited DartPad with Positioned.fill() solution.
I am creating a story app where two users telling the story like this in the below images. so here I want to create a dialog box like the below image. but I don't know how to create
You should be implement below way
class IntroPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _IntroPageState();
}
class _IntroPageState extends State<IntroPage>
with SingleTickerProviderStateMixin {
AnimationController animationController;
bool _menuShown = false;
#override
void initState() {
animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
super.initState();
}
#override
Widget build(BuildContext context) {
Animation opacityAnimation =
Tween(begin: 0.0, end: 1.0).animate(animationController);
if (_menuShown)
animationController.forward();
else
animationController.reverse();
return Scaffold(
backgroundColor: Colors.amberAccent,
body: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
right: 0,
top:90,
child: InkWell(
onTap: () {
setState(() {
_menuShown = !_menuShown;
});
},
child: Image.asset(
'assets/images/girls.png',
height: 250,
),
),
),
Positioned(
child: FadeTransition(
opacity: opacityAnimation,
child: _DialogUI(),
),
right: 40.0,
top: 300.0,
),
],
),
);
}
}
class _DialogUI extends StatelessWidget {
_DialogUI();
final double padding = 8.0;
#override
Widget build(BuildContext context) {
return Center(
child: Material(
clipBehavior: Clip.antiAlias,
shape: _DialogShapeBorder(
borderRadius: BorderRadius.all(Radius.circular(padding)),
padding: padding),
elevation: 4.0,
child: Container(
margin: const EdgeInsets.all(10),
padding: EdgeInsets.all(padding).copyWith(bottom: padding * 2),
child: Center(
child: Text(
'Filler text is text that shares \nsome characteristics of a real written text, \n but is random or otherwise generated.\n It may be used to display a sample of fonts,\n generate text for testing, or to spoof an e-mail spam filter.'),
),
)),
);
}
}
class _DialogShapeBorder extends RoundedRectangleBorder {
_DialogShapeBorder({
#required this.padding,
side = BorderSide.none,
borderRadius = BorderRadius.zero,
}) : super(side: side, borderRadius: borderRadius);
final double padding;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return Path()
..moveTo(rect.width - 18.0, rect.top)
..lineTo(rect.width - 20.0, rect.top - 36.0)
..lineTo(rect.width - 52.0, rect.top)
..addRRect(borderRadius.resolve(textDirection).toRRect(Rect.fromLTWH(
rect.left, rect.top, rect.width, rect.height - padding)));
}
}
Output
I need to put a gradient on my Icon.
How do I achieve this?
One way to do this is to use a ShaderMask widget. If you wrap an icon with this, you can apply any gradient you like:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: LinearGradientMask(
child: Icon(
Icons.book,
size: 250,
color: Colors.white,
),
),
),
),
);
}
}
class LinearGradientMask extends StatelessWidget {
LinearGradientMask({this.child});
final Widget child;
#override
Widget build(BuildContext context) {
return ShaderMask(
shaderCallback: (bounds) {
return RadialGradient(
center: Alignment.topLeft,
radius: 1,
colors: [Colors.blue, Colors.red],
tileMode: TileMode.mirror,
).createShader(bounds);
},
child: child,
);
}
}
giving you something like looks like this:
You can also do this:
Container(
height: 50,
width: 50,
child: Icon(Icons.error, color: Colors.white),
decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.yellow, Colors.black])),
),
I've started playing with Flutter and now thinking about the best way how I can implement a card's flipping animation.
I found this flip_card package and I'm trying to adjust its source code to my needs.
Here is the app which I have now:
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(FlipAnimationApp());
class FlipAnimationApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Flip animation"),
),
body: Center(
child: Container(
width: 200,
height: 200,
child: WidgetFlipper(
frontWidget: Container(
color: Colors.green[200],
child: Center(
child: Text(
"FRONT side.",
),
),
),
backWidget: Container(
color: Colors.yellow[200],
child: Center(
child: Text(
"BACK side.",
),
),
),
),
),
),
),
);
}
}
class WidgetFlipper extends StatefulWidget {
WidgetFlipper({
Key key,
this.frontWidget,
this.backWidget,
}) : super(key: key);
final Widget frontWidget;
final Widget backWidget;
#override
_WidgetFlipperState createState() => _WidgetFlipperState();
}
class _WidgetFlipperState extends State<WidgetFlipper> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> _frontRotation;
Animation<double> _backRotation;
bool isFrontVisible = true;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 500), vsync: this);
_frontRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: pi / 2).chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
],
).animate(controller);
_backRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween(begin: -pi / 2, end: 0.0).chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
],
).animate(controller);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
AnimatedCard(
animation: _backRotation,
child: widget.backWidget,
),
AnimatedCard(
animation: _frontRotation,
child: widget.frontWidget,
),
_tapDetectionControls(),
],
);
}
Widget _tapDetectionControls() {
return Stack(
fit: StackFit.expand,
children: <Widget>[
GestureDetector(
onTap: _leftRotation,
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 1.0,
alignment: Alignment.topLeft,
child: Container(
color: Colors.transparent,
),
),
),
GestureDetector(
onTap: _rightRotation,
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 1.0,
alignment: Alignment.topRight,
child: Container(
color: Colors.transparent,
),
),
),
],
);
}
void _leftRotation() {
_toggleSide();
}
void _rightRotation() {
_toggleSide();
}
void _toggleSide() {
if (isFrontVisible) {
controller.forward();
isFrontVisible = false;
} else {
controller.reverse();
isFrontVisible = true;
}
}
}
class AnimatedCard extends StatelessWidget {
AnimatedCard({
this.child,
this.animation,
});
final Widget child;
final Animation<double> animation;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
var transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
transform.rotateY(animation.value);
return Transform(
transform: transform,
alignment: Alignment.center,
child: child,
);
},
child: child,
);
}
}
Here is how it looks like:
What I'd like to achieve is to make the card flip over its right side if it was tapped on its right half and over its left side if it was tapped on its left half. If it is tapped several times in a row on the same half it should flip over the same side (not back and forth as it is doing now).
So the desired animation should behave as the following one from Quizlet app.
You should know when you tap on the right or left to change the animations dynamically, for that you could use a flag isRightTap. Then, you should invert the values of the Tweens if it has to rotate to one side or to the other.
And the side you should rotate would be:
Rotate to left if the front is visible and you tapped on the left, or, because the back animation is reversed, if the back is is visible and you tapped on the right
Otherwise, rotate to right
Here are the things I changed in _WidgetFlipperState from the code in the question:
_updateRotations(bool isRightTap) {
setState(() {
bool rotateToLeft = (isFrontVisible && !isRightTap) || !isFrontVisible && isRightTap;
_frontRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: rotateToLeft ? (pi / 2) : (-pi / 2))
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(rotateToLeft ? (-pi / 2) : (pi / 2)),
weight: 50.0,
),
],
).animate(controller);
_backRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: ConstantTween<double>(rotateToLeft ? (pi / 2) : (-pi / 2)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween(begin: rotateToLeft ? (-pi / 2) : (pi / 2), end: 0.0)
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
],
).animate(controller);
});
}
#override
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(milliseconds: 500), vsync: this);
_updateRotations(true);
}
void _leftRotation() {
_toggleSide(false);
}
void _rightRotation() {
_toggleSide(true);
}
void _toggleSide(bool isRightTap) {
_updateRotations(isRightTap);
if (isFrontVisible) {
controller.forward();
isFrontVisible = false;
} else {
controller.reverse();
isFrontVisible = true;
}
}
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp();
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage();
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _toggler = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(actions: [
TextButton(
onPressed: _onFlipCardPressed,
child: const Text('change', style: TextStyle(color: Colors.white)),
)
]),
body: Center(
child: SizedBox.square(
dimension: 140,
child: FlipCard(
toggler: _toggler,
frontCard: AppCard(title: 'Front'),
backCard: AppCard(title: 'Back'),
),
),
),
);
}
void _onFlipCardPressed() {
setState(() {
_toggler = !_toggler;
});
}
}
class AppCard extends StatelessWidget {
final String title;
const AppCard({
required this.title,
});
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.deepPurple[400],
),
child: Center(
child: Text(
title,
style: const TextStyle(
fontSize: 40.0,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
);
}
}
class FlipCard extends StatelessWidget {
final bool toggler;
final Widget frontCard;
final Widget backCard;
const FlipCard({
required this.toggler,
required this.backCard,
required this.frontCard,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 800),
transitionBuilder: _transitionBuilder,
layoutBuilder: (widget, list) => Stack(children: [widget!, ...list]),
switchInCurve: Curves.ease,
switchOutCurve: Curves.ease.flipped,
child: toggler
? SizedBox(key: const ValueKey('front'), child: frontCard)
: SizedBox(key: const ValueKey('back'), child: backCard),
),
);
}
Widget _transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnimation = Tween(begin: pi, end: 0.0).animate(animation);
return AnimatedBuilder(
animation: rotateAnimation,
child: widget,
builder: (context, widget) {
final isFront = ValueKey(toggler) == widget!.key;
final rotationY = isFront ? rotateAnimation.value : min(rotateAnimation.value, pi * 0.5);
return Transform(
transform: Matrix4.rotationY(rotationY)..setEntry(3, 0, 0),
alignment: Alignment.center,
child: widget,
);
},
);
}
}
Try this code I've made some changes to your code, now the GestureDetector is divided equally in width on widget so when you tap on the left side of the box it will reverse the animation and if you tap on right side part it will forward the animation.
Widget _tapDetectionControls() {
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: GestureDetector(
onTap: _leftRotation,
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: _rightRotation,
),
),
],
);
}
void _leftRotation() {
controller.reverse();
}
void _rightRotation() {
controller.forward();
}