I am learning flutter.
I created an animation (.svg file) and I want to use it like a button.
(Ontap => run animation)
Any key or link to resolve this?
Thank a lot!
I created an animation (.svg file). I am looking for a way to use it like a button.
Tap on it, run the animation.
I want to use bamboo_tube_animation instead of bamboo_tube .
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State createState() => _Home();
}
class _Home extends State {
Color background_color = Colors.red;
String bamboo_tube = 'assets/images/icons/OngQue.png';
String bamboo_tube_animation = 'assets/animations/OngQue.svg';
#override
Widget build(BuildContext context) {
double device_width = MediaQuery.of(context).size.width;
double device_height = MediaQuery.of(context).size.height;
double bamboo_button_size = device_width * 0.9;
double icon_size = 60;
return Container(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: Container(
child: IconButton(
icon: Image.asset(bamboo_tube),
iconSize: bamboo_button_size,
onPressed: () {},
),
),
),
Container(
height: 20,
),
],
),
);
}
}
use flutter_svg
https://pub.dev/packages/animated_svg
and wrap it with GestureDetector
ex:
GestureDetector(
onTap: () {
// do something
}
child: SvgPicture.asset('/assets/example.svg'),
)
Related
In my app I generate a random number between 1-10 and i try to guess. I use container and text and gesture detector for it. I want containers to change color if i click on the right number which i generated randomly. But I don't know why i does not work i tried to solve but i could not. I used initstate or late variable but did not work. help me?
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key})
: super(
key: key,
);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Random random = Random();
late int guessNumber = random.nextInt(9) + 1;
#override
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My App"),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Row(
children: [
Expanded(child: Numb(1)),
Expanded(child: Numb(2)),
Expanded(child: Numb(3)),
],
),
),
Expanded(
child: Row(
children: [
Expanded(child: Numb(4)),
Expanded(child: Numb(5)),
Expanded(child: Numb(6)),
],
),
),
Expanded(
child: Row(
children: [
Expanded(child: Numb(7)),
Expanded(child: Numb(8)),
Expanded(child: Numb(9)),
],
),
),
],
));
}
Widget Numb(int numb) {
Color? color = Colors.lightGreen;
return GestureDetector(
onTap: () {
setState(() {
if (guessNumber == numb) {
color = Colors.pink;
}
});
},
child: Container(
margin: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: color,
),
child: Center(
child: Text(
numb.toString(),
style: const TextStyle(fontSize: 24),
),
),
),
);
}
}
The issue is color is inside the build method(while Numb(int numb) is inside build method) and keep getting Colors.lightGreen; on every setState. Put it outside the build method. like on
class _HomeScreenState extends State<HomeScreen> {
Random random = Random();
late int guessNumber = random.nextInt(9) + 1;
Color? color = Colors.lightGreen;
I have two screens where the second screen is pushed above the first with Navigator.push() and the second screen is partial transparent. I want to display a SnackBar, but it isn't really visible. It looks like the ScaffoldMessenger chooses the wrong of the two Scaffolds to attach the Snackbar. This leads to the effect that the SnackBar collides with the TextInput and it is also not fully visible. But this bad behavior is only the case as long as the soft keyboard is open. If the keyboard is closed, everything works fine. It seems like the open keyboard tells the ScaffoldMessenger to choose the Scaffold from the second screen to display the SnackBar.
How can I achieve that the SnackBar is shown normally in the sense of is attached to the Scaffold of screen 2 while the keyboard is open? The expected behavior is that the Snackbar isn't displayed transparent.
Keyboard open -> SnackBar is attached to Scaffold of screen 1 -> Bad
Keyboard closed -> SnackBar is attached to Scaffold of screen 2 -> Good
GIF showing the complete workflow
My code (fully executable)
import 'dart:io';
import 'package:keyboard_utils/keyboard_listener.dart';
import 'package:keyboard_utils/keyboard_utils.dart';
import 'package:flutter/material.dart' hide KeyboardListener;
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => const MaterialApp(home: MyHomePage());
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Title')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[const Text('You have pushed the button this many times:'),
Text('$_counter', style: Theme.of(context).textTheme.headline4),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(PageRouteBuilder(
opaque: false, // push route with transparency
pageBuilder: (context, animation, secondaryAnimation) => const Screen2(),
));
},
child: const Text('navigate'),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _counter++),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Screen2 extends StatefulWidget {
const Screen2({Key? key}) : super(key: key);
#override
State<Screen2> createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
final _keyboardUtils = KeyboardUtils();
late int _idKeyboardListener;
final focusNode = FocusNode();
bool isEmojiKeyboardVisible = false;
bool isKeyboardVisible = false;
double _keyboardHeight = 300;
#override
void initState() {
super.initState();
_idKeyboardListener = _keyboardUtils.add(
listener: KeyboardListener(
willHideKeyboard: () {
if (isKeyboardVisible) {
isKeyboardVisible = false;
isEmojiKeyboardVisible = false;
}
setState(() {}); // show correct Icon in IconButton
},
willShowKeyboard: (double keyboardHeight) async {
if (Platform.isAndroid) {
_keyboardHeight = keyboardHeight + WidgetsBinding.instance.window.viewPadding.top / WidgetsBinding.instance.window.devicePixelRatio;
} else {
_keyboardHeight = keyboardHeight;
}
isKeyboardVisible = true;
isEmojiKeyboardVisible = true;
setState(() {});
},
)
);
}
#override
void dispose() {
_keyboardUtils.unsubscribeListener(subscribingId: _idKeyboardListener);
if (_keyboardUtils.canCallDispose()) {
_keyboardUtils.dispose();
}
focusNode.dispose();
super.dispose();
}
Future<void> onEmojiButtonPressed() async {
if(isEmojiKeyboardVisible){
if(isKeyboardVisible){
FocusManager.instance.primaryFocus?.unfocus();
isKeyboardVisible = false;
} else {
focusNode.unfocus();
await Future<void>.delayed(const Duration(milliseconds: 1));
if(!mounted) return;
FocusScope.of(context).requestFocus(focusNode);
}
} else {
assert(!isKeyboardVisible);
setState(() {
isEmojiKeyboardVisible = true;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold( // wrapping with ScaffoldMessenger does NOT fix this bug
backgroundColor: Colors.white.withOpacity(0.5),
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Expanded(
child: Container(
height: 200,
),
),
Row(
children: [
IconButton(
icon: Icon(isKeyboardVisible || !isEmojiKeyboardVisible ? Icons.emoji_emotions_outlined : Icons.keyboard_rounded),
onPressed: onEmojiButtonPressed,
),
Expanded(
child: TextField(
focusNode: focusNode,
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('A snack!'))),
),
],
),
],
),
),
),
Offstage(
offstage: !isEmojiKeyboardVisible,
child: SizedBox(
height: _keyboardHeight,
child: Container(color: Colors.red),
),
),
],
),
),
);
}
}
Dependencies
keyboard_utils: ^1.3.4
What I've tried
I tried to wrap the Scaffold of Screen2 with a ScaffoldMessenger. This doesn't fix my problem. In that case, no SnackBar was shown at all if the keyboard was open.
Edit: I also created an GitHub issue for that but I don't expect an answer soon: https://github.com/flutter/flutter/issues/105406#issuecomment-1147194647
Edit 2: A workaround for this issue is to use SnackBarBehaviod.floating and a bottom margin, for example:
SnackBar(
content: Text('A snack!'),
margin: EdgeInsets.only(bottom: 350.0),
behavior: SnackBarBehavior.floating,
)
But this is not a satisfying solution.
How to clip Stack children within it's size.
In this image there are 3 grid-Items using orange color and every item using InkWell to use hover-Method to Align on Stack. While hover:false the Pop PoP Widget won't be visible to the UI. With align property it works, but as you can see the Right Top GridItem's item:2 pop POp widget is visible outside the Stack<Griditem> and I want to make it invisible outside the stack. I've tested using clipBehavior: with every Clip enums.
I want to hide the Pop POp widget while it is outside the Stack and yes I need this pop-up effect.
For Flutter web and I'm using Flutter V2.5.2
Current Layout with Issue
Full Code to reproduce the issue
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(
home: Appp(),
),
);
class Appp extends StatelessWidget {
const Appp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const BodyX();
}
}
class BodyX extends StatelessWidget {
const BodyX({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
return GridView.count(
crossAxisCount: 2,
children: [
...List.generate(
3,
(index) => GridItem(
key: UniqueKey(),
maxWidth: constraints.maxWidth / 2,
),
),
],
);
},
));
}
}
class GridItem extends StatefulWidget {
const GridItem({
Key? key,
required this.maxWidth,
}) : super(key: key);
final double maxWidth;
#override
State<GridItem> createState() => _AppXState();
}
class _AppXState extends State<GridItem> {
bool _isHovered = false;
#override
Widget build(BuildContext context) {
print("ItemWidth : ${widget.maxWidth}");
return SizedBox(
//though it wont effect here,
// just finding the size of Grid because it will 1x1
width: widget.maxWidth,
height: widget.maxWidth,
child: InkWell(
onTap: () {},
hoverColor: Colors.black,
onHover: (value) {
setState(() {
_isHovered = value;
});
},
child: Stack(
clipBehavior: Clip.antiAliasWithSaveLayer,
children: [
Container(
color: Colors.deepOrange.withOpacity(.2),
),
AnimatedAlign(
alignment: Alignment(0, _isHovered ? .7 : 2),
child: Container(
padding: const EdgeInsets.all(22),
color: Colors.greenAccent,
child: const Text(
"Pop POp",
),
),
duration: const Duration(
milliseconds: 200,
),
)
],
),
),
);
}
}
If you don't want a Widget to draw beyond its layout size, you can use ClipRect to clip it.
In your case, you can wrap ClipRect on your Stack, like so:
ClipRect(
child: Stack(
children: ...
),
)
Further more, you can use ClipRRect to clip a rounded rectangle shape (circular border) or ClipPath to clip a custom shape, like a triangle. You can read more about these widgets in the official docs.
I am using a CustomScrollView and trying to add a like button in the SliverAppBar actions which by tapping on it, its shape changes. I am making the changes inside setState. But despite that the build method is being called, UI is not being updated.
TextButton(
onPressed: () {
bool added = courseStore.addToUserFavoriteCourses(widget.courseDetails);
added ?
setState(() {
iconData = Icons.favorite;
}) :
setState(() {
iconData = Icons.favorite_border;
});
},
child: Icon(
iconData,
size: 20,
color: Colors.white,
),
)
The above code is not saving state of iconData yet.
The state of like/unlike needs to be moved up into a parent widget scope. Its value can then be referenced below in children like TextButton.
When TextButton is rebuilt (changing from unlike to like & back) it can use the state that persists above it.
Notice below how iconData lives in the State widget. This widget designed to hold state for its children.
You can copy paste this code into a Flutter/Dart file to test:
import 'package:flutter/material.dart';
// ↓ Use this as your button
class StatefulButton extends StatefulWidget {
final double size;
final Color color;
StatefulButton({this.size = 20, this.color = Colors.black});
#override
_StatefulButtonState createState() => _StatefulButtonState();
}
class _StatefulButtonState extends State<StatefulButton> {
IconData iconData = Icons.favorite_border;
// ↑ Move your state up to here
#override
Widget build(BuildContext context) {
return Center(
child: TextButton(
child: Icon(
iconData,
size: widget.size,
color: widget.color,
),
onPressed: toggleLike,
),
);
}
void toggleLike() {
setState(() {
iconData = iconData == Icons.favorite ? Icons.favorite_border : Icons.favorite;
});
}
}
/// Just a container page for the example above
class TextButtonStatePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextButtonState'),
),
body: StatefulButton()
);
}
}
Here's an example of using the StatefulButton above inside a CustomScrollView. The CustomScrollView code I stole straight from the Flutter docs website. The only edit I've made is to add the StatefulButton above into each sliver, to show it changing its state when clicked.
/// Flutter code sample for CustomScrollView
import 'package:flutter/material.dart';
import 'text_button_state.dart';
/// This is the stateful widget that the main application instantiates.
class CustomScrollviewPage extends StatefulWidget {
CustomScrollviewPage({Key key}) : super(key: key);
#override
_CustomScrollviewPageState createState() => _CustomScrollviewPageState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _CustomScrollviewPageState extends State<CustomScrollviewPage> {
List<int> top = [];
List<int> bottom = [0];
#override
Widget build(BuildContext context) {
const Key centerKey = ValueKey('bottom-sliver-list');
return Scaffold(
appBar: AppBar(
title: const Text('Press on the plus to add items above and below'),
leading: IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
top.add(-top.length - 1);
bottom.add(bottom.length);
});
},
),
),
body: CustomScrollView(
center: centerKey,
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.blue[200 + top[index] % 4 * 100],
height: 100 + top[index] % 4 * 20.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Item: ${top[index]}'),
StatefulButton() // ← STATEFUL BUTTON HERE
],),
);
},
childCount: top.length,
),
),
SliverList(
key: centerKey,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.blue[200 + bottom[index] % 4 * 100],
height: 100 + bottom[index] % 4 * 20.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Item: ${bottom[index]}'),
StatefulButton() // ← STATEFUL BUTTON HERE
],
),
);
},
childCount: bottom.length,
),
),
],
),
);
}
}
I'm creating a music player and I need the music controls don't reinitialize or disappear on screen changing. If I add the code on another screen it will create another FloatingControls() widget.
I've already tried work with keys but that isn't the case because the Widget is recreated when I change screens.
As you can see my FloatingControls has a Widget called YoutubePlayer when I press play a video starts when I change screens I want that the player doesn't restart.
FloatingControls myFloatingControls = FloatingControls(key: Key("myFloatingControls"),);
class MusicSuggestions extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'MusicSuggestions',
home: new MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
const MainScreen({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text("Change to Screen A"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ScreenA(floatingControls: myFloatingControls),
),
);
},
),
FlatButton(
child: Text("Change to Screen B"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ScreenB(floatingControls: myFloatingControls),
),
);
},
),
],
),
myFloatingControls
],
),
),
);
}
}
class ScreenA extends StatefulWidget {
final FloatingControls floatingControls;
const ScreenA({Key key, this.floatingControls}) : super(key: key);
#override
_ScreenAState createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: widget.floatingControls,));
}
}
class ScreenB extends StatefulWidget {
final FloatingControls floatingControls;
const ScreenB({Key key, this.floatingControls}) : super(key: key);
#override
_ScreenBState createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: widget.floatingControls,));
}
}
class FloatingControls extends StatefulWidget {
const FloatingControls({Key key}) : super(key: key);
#override
_FloatingControlsState createState() => _FloatingControlsState();
}
class _FloatingControlsState extends State<FloatingControls> {
VideoPlayerController _videoController;
bool isMute = false;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
ClipOval(
child: Container(
width: 50,
height: 50,
child: YoutubePlayer(
autoPlay: false,
aspectRatio: 1,
width: 50,
context: context,
playerMode: YoutubePlayerMode.NO_CONTROLS,
source: "https://www.youtube.com/watch?v=PodZolu9v30",
quality: YoutubeQuality.LOW,
callbackController: (VideoPlayerController controller) {
_videoController = controller;
},
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () {},
),
IconButton(
icon: _videoController == null ||
!_videoController.value.isPlaying
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
onPressed: () {
setState(() {
_videoController == null ||
_videoController.value.isPlaying
? _videoController.pause()
: _videoController.play();
});
}),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () {},
),
Container(
width: 25,
height: 25,
)
],
),
]);
}
}
I expect to see my FloatingControls() in all screens without losing its state when I change pages.
Make sure to add floatingControls to your ScreenA and ScreenB build methods.
Example:
class _ScreenAState extends State<ScreenA> {
#override
Widget build(BuildContext context) {
return widget.floatingControls();
}
}
If you do this for both ScreenA and ScreenB, it should work.
--EDIT--
You should look into the PageStorage and PageStorageBucket classes, which will help you persist state across rebuilds. I don't have a lot of experience with these, so instead of giving you a potentially shoddy code snippet to copy, I will direct you to this tutorial by Tensor Programming (whose tutorials have helped me tremendously) which should help you do what you need to do. They are doing it with navigation bars, but it should extend easily to what you're doing.