I'm trying to create a parallax background for page controller. For that purpuse I need to create a background image that is wider than the screen. I've put it inside a container like this:
#override
Widget build(BuildContext context) {
return Material(
child: Stack(
children: [
Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat
)
)
),
],
),
);
}
But the problem is that no matter what width I specify, the container (and the image, of course) never get wider than the screen. Is it possible at all?
p.s. I tried to use SizedBox and AspectRatio widgets, and they both give the same result
try this, as an option
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
],
),
),
);
}
}
also you can disable scroll for user and manage scroll position via scroll controller
SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
controller: controller, // your ScrollController
child: Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
For images you can use Transform.scale(), as found in the documentation. Using your example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: [
Align(
alignment: Alignment.center,
child: Transform.scale(
scale: 10.0,
child: Container(
width: 400,
height: 25,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
),
],
),
),
);
}
}
If you want to animate the scale, you can use ScaleTransition(), explained in this page of the docs. For example:
/// Flutter code sample for ScaleTransition
// The following code implements the [ScaleTransition] as seen in the video
// above:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
/// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ScaleTransition(
scale: _animation,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
),
),
);
}
}
NOTE: To avoid quality loss in the image, use an image of the size after scaling or a vector graphic as a source.
Related
My image size is quite small which is about 50kb but it seems to load longer than I expected it would be, so my app doesn't load up that prettily as I would imagined. Any way I could speed it up?
`
import 'dart:async';
import 'package:egarment2/pages/home_page.dart';
import 'package:egarment2/sign_in.dart';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
// void initState() {
// super.initState();
// Timer(
// const Duration(seconds: 3),
// () {
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(
// builder: (context) => HomePage(),
// ),
// );
// },
// );
// }
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
// color: const Color(0xFFf65d46),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg.jpg'),
fit: BoxFit.fill,
),
),
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/logo.png',
width: 75,
height: 75,
),
const SizedBox(
height: 25,
),
const Text(
'E-Garment',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30),
),
],
),
),
);
}
}
`
I tried compressing the image size which originally was 200 something kb to 50kb (current size) but it doesn't seem to do anything.
You can use the method precacheImage:
class _SplashScreenState extends State<SplashScreen> {
final _image = AssetImage('assets/images/bg.jpg');
#override
void initState() {
super.initState();
precacheImage(_image, context);
// Add your own initState code here
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(_image, context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: _image,
fit: BoxFit.fill,
),
),
// ...
),
);
}
}
So, I have bigger scroll view that scrolls horizontally, and inside - little box (red color) and smaller scrollview (orange color) that scrolls vertically.
There are two scrollbars on the bigger scrollview (1 - for horizontal), and second - for vertical inner.
And the problem - vertical scrollbar doesnt look right, because it can go only like blue arrow shows, and I want it to have either full height of the bigger scrollview, or be right near scrollable vertical part, but then dont hide itself if scrolled in horizontal direction.
Run on dartPad
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ScrollSizingWidget(),
),
),
);
}
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({
Key? key,
}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
final ScrollController _horizontal = ScrollController();
final ScrollController _vertical = ScrollController();
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scrollbar(
controller: _vertical,
notificationPredicate: (notification) => notification.depth == 1,
child: Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children:[
Container(width: 1000, height: 200, color: Colors.green),
Flexible(
child: SingleChildScrollView(
controller: _vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.yellow,
)
)
),
]
)
),
),
),
);
}
}
I have used your code to reproduce the issue. If I understood your needs right, here is the fix:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Container(
height: 1000,
width: 1000,
color: Colors.orange,
),
],
),
),
),
),
),
);
}
Firstly, you say that your main SingleChildScrollView scrolls horizontally, but your widget tree starts with a Scrollbar which uses a vertical ScrollController. So you should create your widgets step by step, as you explained.
Also, since you want to see the vertical Scrollbar through your main SingleChildScrollView, I wrapped both(red and orange Containers) with Scrollbar and SingleChildScrollView to have the effect you want. Furthermore, I connected these Scrollbar and SingleChildScrollView with the same horizontal ScrollController. So now, not only the orange Container, but both are scrollable and stick together, not independent.
If you don't want the red Container being scrolled along with the orange Container, check this:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Expanded(
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.orange,
),
),
),
),
],
),
),
),
);
}
Lastly, Scrollbar's position in iOS is a bit buggy because of the notch, etc. So I wrapped your ScrollSizingWidget with SafeArea to fix the issue in iOS.
If these answers are not what you expect, please don't hesitate to write.
Edit: After your explanations in the comments below, I have created another fix. I believe CustomScrollView and Sliver widgets are fits here perfectly. The red Container, which you want to stay in its position, should be wrapped with the SliverAppBar. Lastly, the orange Container, which you want to be able to scroll vertically, could be wrapped with SliverFixedExtentList. Please check the code below:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
#override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
#override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
#override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: CustomScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
slivers: [
SliverAppBar(
toolbarHeight: 200.0,
collapsedHeight: 200.0,
pinned: true,
stretch: true,
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Container(
height: 200.0,
color: Colors.red,
),
titleSpacing: 0,
),
SliverFixedExtentList(
itemExtent: 1200.0,
delegate: SliverChildBuilderDelegate(
(_, __) => Container(
color: Colors.orange,
),
childCount: 1,
),
),
],
),
),
),
),
);
}
I'm trying to put together a simple template as a learning exercise. I want the image width to occupy the screen side to side, and the height to be clipped at 200px hiding everything in excess.
code:
Image _buildJournalHeaderImage(context) {
return Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
// fit: BoxFit.cover,
fit: BoxFit.fitWidth,
height: MediaQuery.of(context).size.width / 2,
);
}
this method is placed as a column child.
You should not need anything more complex than this to have the image fill the width and have a fixed-height of 200:
import 'package:flutter/material.dart';
class FitToWidthExample extends StatelessWidget {
const FitToWidthExample({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
children: [
Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
fit: BoxFit.fitWidth,
alignment: Alignment.center, // If you don't want the image center aligned modify this.
width: MediaQuery.of(context).size.width,
height: 200,
)
]
);
}
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => const MaterialApp(
home: Scaffold(body: FitToWidthExample()),
);
}
void main() => runApp(const App());
Create like this
Container(
height: 200,
child: Row(
children: [
Expanded(
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
// fit: BoxFit.cover,
fit: BoxFit.fitWidth,
),
),
],
),
)
SampleCode dartpad livecode
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key, this.title}) : super(key: key);
final String? title;
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var column = Column(
children: [
Container(
height: 200,
child: Row(
children: [
Expanded(
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
// fit: BoxFit.cover,
fit: BoxFit.fitWidth,
),
),
],
),
)
],
);
return MaterialApp(
// theme: theme(),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(),
body: Container(
height: 200,
child: Row(
children: [
Expanded(
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Salto_del_Angel-Canaima-Venezuela08.JPG/1200px-Salto_del_Angel-Canaima-Venezuela08.JPG",
// fit: BoxFit.cover,
fit: BoxFit.fitWidth,
),
),
],
),
)
),
);
}
}
We want of to display a list of images and text that we fetch from a database. When you tap on one of the images / texts, they should expand with an animation. With the current code, the expanding animation works as intended, but the opposite does not work. The image shrinks without an animation, and then the AnimatedSize animates. See: Flutter AnimatedSize works in one direction only
Since we don't have a solid color the solution to that question does not really work for us.
It does work with the images with the following code, but it's not ideal and text does not work at all that way.
Container(
foregroundDecoration: BoxDecoration(
image: DecorationImage(
image: CachedNetworkImageProvider(
snapshot.data.documents[index]['image'],
),
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
child: AnimatedSize(
duration: Duration(milliseconds: 300),
vsync: this,
child: Opacity(
opacity: 0,
child: Container(
height: _expandedIndex == index ? null : 250,
child: CachedNetworkImage(
imageUrl: snapshot.data.documents[index]['image'] ?? '',
fit: BoxFit.cover,
width: double.infinity,
),
),
),
),
),
I have found a lot of other people with the same problem, but I have never found a solution that works, so any help is very welcome.
Thanks!
use a sizeTransition Widget.
and set up your animationController and Tween<double>
and it should work fine, I've used it and it worked in both directions forward and reverse.
Tried sizeTransition widget, and it worked for me. AnimationController and Tween are not necessary. Here is my worked code.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
late final Animation<double> _animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextButton(
child: const Text('toggle'),
onPressed: () => _controller.isDismissed
? _controller.forward()
: _controller.reverse(),
),
SizeTransition(
sizeFactor: _animation,
axis: Axis.vertical,
axisAlignment: -1,
child: Container(
color: Colors.amberAccent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [Text("Hello"), Text("World")],
),
),
),
],
),
);
}
}
I would like to make something like this:
https://youtu.be/W3O0077GMlo
And I would like for the rotating circle (moon in this video) to act as a button.
What is the best way to do this performance wise?
You can use the RotationTransition inside a Stack widget to create the rotating animation. Inside the Stackset the alignment to center, and wrap your rotating widget inside an Align. Set the alignment attribute of the Align widget to Alignment.topCenter or any outer alignment.
Remember to deploy on release to your phone to make sure the animations are running smooth.
Quick standalone code example:
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: SizedBox(width: 300.0, height: 300.0, child: OrbitingButton()),
),
),
);
}
}
class OrbitingButton extends StatefulWidget {
#override
_OrbitingButtonState createState() => _OrbitingButtonState();
}
class _OrbitingButtonState extends State<OrbitingButton>
with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(vsync: this);
controller.repeat(min: 0.0, max: 1.0, period: Duration(seconds: 1));
}
#override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
RotationTransition(
turns: controller,
child: Align(
alignment: Alignment.topCenter,
child: Container(
color: Colors.green,
height: 30.0,
width: 30.0,
),
),
),
RaisedButton(
child: Text('Button'),
)
],
);
}
}