Text loses style during hero animation in Flutter - flutter

As you can see from the gif and from the code below I have a container and a text widget, both wrapped in a hero widget.
When the container is clicked the second page opens. I would like to have a hero animation for both widgets.
The animations of the container works great. The text however seems to lose the style while the transition is happening.
Any idea on how to fix it?
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
routes: {
HomeScreen.routName: (context) => const HomeScreen(),
SecondSceen.routeName: (context) => const SecondSceen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
static const routName = '/home-screen';
const HomeScreen({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Align(
alignment: Alignment.bottomCenter,
child: InkWell(
onTap: () => Navigator.of(context).pushNamed(SecondSceen.routeName),
child: Hero(
tag: 'box',
child: Container(
color: Colors.amber,
width: MediaQuery.of(context).size.width * 0.8,
height: 210,
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
height: 210,
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 8, 16),
child: Column(
children: const [
Hero(
tag: 'text',
child: Text(
"MY HEADER",
style:
TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
]),
);
}
}
class SecondSceen extends StatelessWidget {
static const routeName = '/note-screen';
const SecondSceen({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
foregroundColor: Colors.black,
centerTitle: true,
title: const Hero(
tag: 'text',
child: Text(
"MY HEADER",
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
),
),
body: Stack(children: [
Hero(
tag: 'box',
child: Container(
color: Colors.amber,
),
),
]),
);
}
}

Instead of using individual TextStyle objects, use some style coming from the context. This way even during the animation, the styles will be based on the Theme of your MaterialApp.
For both Text widgets, instead of:
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
try:
style: Theme.of(context).textTheme.headline6, // or some other style
(You will need to remove const keywords because these are no longer constants.)
You can easily customize any built in style using copyWith, for example:
style: Theme.of(context).textTheme.headline6!.copyWith(
fontSize: 28,
fontWeight: FontWeight.bold),
In general it is a good practice in my opinion to use Theme styles.

Related

how to add a fullscreen image in flutter

here's my code and i want a fullscreen image with a centerd button but i won't get that result , screenshot of app in below the code
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Kings of Iran',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WelcomePage(),
);
}
}
class WelcomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
"Kings of Iran",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/back.jpg"),
fit: BoxFit.cover,
alignment: Alignment.center)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50.0,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
},
child: Text(
"Explore",
style: TextStyle(
fontSize: 20.0, color: Color.fromARGB(255, 191, 211, 9)),
),
)
],
),
),
);
}
}
and this is the result
How can I make this image fullscreen and button centered?
You can use Stack for display image and display button at center of the screen.
Stack : https://api.flutter.dev/flutter/widgets/Stack-class.html
Stack useful if you want to overlap several children in a simple way, for example having some text and an image, overlaid with a gradient and a button attached to the center.
Example :
class WelcomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
"Kings of Iran",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
body: Stack(
children: [
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/back.jpg"), fit: BoxFit.cover, alignment: Alignment.center))),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50.0,
),
Center(child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
},
child: Text(
"Explore",
style: TextStyle(fontSize: 20.0, color: Color.fromARGB(255, 191, 211, 9)),
),
))
],
),
],
)
}
}
Just add width: MediaQuery.of(context).size.width in Container

Flutter Collapsible Appbar with custom layout and animation

I am trying to create an app bar that looks like the following image when expanded
When collapsing I only need the initial column of text to be available in the app bar, so the image should fade away and the text column to animate and become the Appbar title
I have tried using SliverAppBar with a custom scroll view but the Flexible space's title is not looking good also I don't know how to make the image disappear alone and show only the text when collapsed.
As of now, this is the expanded state
And when I try to collapse it, this happens
Here's the code for the same
CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
leading: GestureDetector(
onTap: () => Get.back(),
child: const Icon(
Icons.arrow_back,
color: Colors.black,
),
),
flexibleSpace: FlexibleSpaceBar(
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
const Expanded(
child: Text.rich(
TextSpan(
text: "Buffet Deal\n",
style:
TextStyle(color: Colors.black, fontSize: 14),
children: [
TextSpan(
text: "Flash Deal",
style: TextStyle(
color: Colors.black, fontSize: 10),
),
]),
),
),
if (_model.imgUrl != null)
CachedNetworkImage(
imageUrl: _model.imgUrl!,
),
const SizedBox(
width: 18,
)
],
),
),
),
expandedHeight: Get.height * 0.2,
backgroundColor: const Color(0xFFFFF4F4)),
SliverToBoxAdapter(
child: Container()
)
],
),
Your help is appreciated. Thanks in advance
To make the SliverAppBar's flexible space image disappear whenever the expanded space collapse just use the FlexibleSpaceBar.background property.
To solve the overlapping text just wrap the FlexibleSpaceBar around a LayoutBuilder to get if it's expanded or not and adjust the text positioning.
Check out the result below (Also, the live demo on DartPad):
import 'dart:math';
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(
title: 'Flutter Demo',
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final expandedHeight = MediaQuery.of(context).size.height * 0.2;
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
leading: GestureDetector(
onTap: () => {},
child: const Icon(
Icons.arrow_back,
color: Colors.black,
),
),
flexibleSpace: LayoutBuilder(builder: (context, constraints) {
final fraction =
max(0, constraints.biggest.height - kToolbarHeight) /
(expandedHeight - kToolbarHeight);
return FlexibleSpaceBar(
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 24 * (1 - fraction)),
child: const Text.rich(
TextSpan(
text: "Buffet Deal\n",
style: TextStyle(
color: Colors.black, fontSize: 14),
children: [
TextSpan(
text: "Flash Deal",
style: TextStyle(
color: Colors.black, fontSize: 10),
),
]),
),
),
),
const SizedBox(width: 18)
],
),
),
background: Align(
alignment: Alignment.centerRight,
child: Image.network(
"https://source.unsplash.com/random/400x225?sig=1&licors",
),
),
);
}),
expandedHeight: MediaQuery.of(context).size.height * 0.2,
backgroundColor: const Color(0xFFFFF4F4),
),
const SliverToBoxAdapter(child: SizedBox(height: 1000))
],
),
);
}
}

Static image with scrollable text in flutter

import 'package:flutter_svg/flutter_svg.dart';
void main() {
runApp(MaterialApp(
home: Scaffold(
// adding App Bar
appBar: AppBar(
actions: [
SvgPicture.asset(
"assets/images/Moto.svg",
width: 50,
height: 100,
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.cancel_outlined),
alignment: Alignment.topRight,
)
],
backgroundColor: Colors.white,
title: const Text(
"Version:1.38-alpha(10308)",
style: TextStyle(
color: Colors.white,
),
),
),
body: const MyApp(),
),
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: const Expanded(
// SingleChildScrollView contains a
// single child which is scrollable
child: SingleChildScrollView(
// for Vertical scrolling
scrollDirection: Axis.vertical,
child: Text(`
This is simple wrap your Expanded widget with stack and add the image widget as a first child to the stack.
Scaffold(
appBar: AppBar(
title: const Text('Sample UI'),
),
body: Stack(
children: [
SizedBox.expand(
child: Image.network(
'https://images.pexels.com/photos/1624496/pexels-photo-1624496.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
fit: BoxFit.cover,
),
),
SingleChildScrollView(
child: Column(
children: List.generate(100,(index) =>
Text(
'Hello welcome $index',
style: const TextStyle(
fontSize: 20,
color: Colors.amberAccent,
fontWeight: FontWeight.bold,
),
)
),
),
)
],
),
)

Flutter Drawer when menu icon is tapped

I am currently trying out drawer widget. I have constructed an appbar with a hamburger menu icon, title text and a search icon.
Here is my code:
`
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
child: Container(
color: Colors.white,
padding:
EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Icon(Icons.menu),
Text(
'Appbar',
style: TextStyle(fontWeight: FontWeight.bold),
),
Icon(Icons.search)
],
),
),
preferredSize: Size.fromHeight(40)),
backgroundColor: Hexcolor('#e9f1fe'),
body: Center(
child: Text('Experimenting drawer'),
)));
}
}
`
Here is the output :,
Also I have a separate dart file for a custom drawer I want to be displayed when the hamburger menu icon is tapped. How can I make that possible?
Here is the code for custom drawer:
import 'package:flutter/material.dart';
class DrawerScreen extends StatefulWidget {
#override
_DrawerScreenState createState() => _DrawerScreenState();
}
class _DrawerScreenState extends State<DrawerScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: Column(
children: [
ListTile(
leading: CircleAvatar(
radius: 28,
backgroundColor: Colors.blueGrey,
child: CircleAvatar(
radius: 50,
backgroundImage: AssetImage('assets/images/joker.jpg'),
),
),
title: Text(
'Joaquin Phoenix',
style: TextStyle(
color: Colors.black,
fontFamily: 'Roboto',
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
subtitle: Text(
"You wouldn't get it",
style: TextStyle(
color: Colors.black,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
fontSize: 10.0),
),
)
],
)),
backgroundColor: Colors.blue[50],
);
}
}
You can copy paste run two full code below
Step 1: _DrawerScreenState remove Scaffold
Step 2: For drawer background color, you can wrap with Theme
class _DrawerScreenState extends State<DrawerScreen> {
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.blue[50],
),
child: Drawer(
Step 3: Use DrawerScreen() in main.dart
SafeArea(
child: Scaffold(
drawer: DrawerScreen(),
Step 4: wrap Icons.menu with Builder and open with openDrawer()
Builder(
builder: (context) => GestureDetector(
onTap: () {
Scaffold.of(context).openDrawer();
},
child: Icon(Icons.menu)),
),
working demo
full code main.dart
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
import 'drawer.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
drawer: DrawerScreen(),
appBar: PreferredSize(
child: Container(
color: Colors.white,
padding:
EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Builder(
builder: (context) => GestureDetector(
onTap: () {
Scaffold.of(context).openDrawer();
},
child: Icon(Icons.menu)),
),
Text(
'Appbar',
style: TextStyle(fontWeight: FontWeight.bold),
),
Icon(Icons.search)
],
),
),
preferredSize: Size.fromHeight(40)),
backgroundColor: Hexcolor('#e9f1fe'),
body: Center(
child: Text('Experimenting drawer'),
)));
}
}
full code drawer.dart
import 'package:flutter/material.dart';
class DrawerScreen extends StatefulWidget {
#override
_DrawerScreenState createState() => _DrawerScreenState();
}
class _DrawerScreenState extends State<DrawerScreen> {
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.blue[50],
),
child: Drawer(
child: Column(
children: [
ListTile(
leading: CircleAvatar(
radius: 28,
backgroundColor: Colors.blueGrey,
child: CircleAvatar(
radius: 50,
backgroundImage: AssetImage('assets/images/joker.jpg'),
),
),
title: Text(
'Joaquin Phoenix',
style: TextStyle(
color: Colors.black,
fontFamily: 'Roboto',
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
subtitle: Text(
"You wouldn't get it",
style: TextStyle(
color: Colors.black,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
fontSize: 10.0),
),
)
],
)),
);
}
}

Styling of Cupertino Picker in Flutter

I have tried to implement a layout like this using flutter.
I have used the Cupertino Picker Class for that.
But it just looks not as good as I want it to look.
I have tried to fix it by using the properties, that are listed on the flutter website, but I did not find any better solution. How can I fix this issue or is there even another widget that is better in this case?
My code looks like this:
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: Colors.red,
),
child: Container(
child: Center(
child: CupertinoPicker(
itemExtent: 40,
diameterRatio: 5,
offAxisFraction: 10,
backgroundColor: Colors.black54,
onSelectedItemChanged: (int index) {
print(index);
},
children: <Widget>[
Text('Tap', style: TextStyle(color:Colors.white)),
Text('Colorbattle',style: TextStyle(color:Colors.white)),
Text('Patience',style: TextStyle(color:Colors.white)),
],
))));
}
}
Playing around your code, I've tried to implement the same behavior. I've observed that changing the values of diameterRatio and offAxisFraction could change the way your UI look like. In my sample, the values of diameterRatio and offAxisFraction is 10 and -1 respectively. Just imagine this kind of roulette:
diameterRatio is the diameter of the cylinder and offAxisFraction is the axis where the side of the cylinder is facing. To mimic the example in the picture, set the value of the offAxisFraction to a positive value, in the implementation shown below, I've set it to 10.
Below is an example of copy the behavior of the UI you're trying to replicate:
import 'package:flutter/cupertino.dart';
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: Colors.red,
),
child: Center(
child: Container(
// color: Colors.black54,
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
width: 200.0,
height: 150.0,
child: Center(
child: CupertinoPicker(
itemExtent: 40,
diameterRatio: 10,
offAxisFraction: -1,
backgroundColor: Colors.black54,
onSelectedItemChanged: (int index) {
print(index);
},
children: <Widget>[
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 40.0),
child: Text(
'Estimate',
style: TextStyle(color: Colors.white),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 40.0),
child: Text(
'Random',
style: TextStyle(color: Colors.white),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 40.0),
child: Text(
'Colorbattle',
style: TextStyle(color: Colors.white),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 40.0),
child: Text(
'Patience',
style: TextStyle(color: Colors.white),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 40.0),
child: Text(
'Tap',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
),
);
}
}
Output: