Masked Blur for Image - flutter

I would like to implement the effect shown below in which most parts of an image are blurred and only the interesting part of the image is shown crisp (with a border).
I am not sure how to implement the effect and would like to consider your ideas. Blurring the image is not the problem (i am doing it as described here), but leaving a part of the image non-blurred is quite a challenge. I thought about layering two images (one blurred and one non-blurred) over each other. However that will not work because then the blurred image will cast a shadow over the cutout section.
I also tried to use a CustomPainter, however i failed to get the blur working.

This is how you can do it , The idea is to make CustomClipper to invert the behavior of the BackDropFilter.
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
height: 250,
child: Stack(
children: <Widget>[
Center(
child: Image.network(
'https://cdn-prod.medicalnewstoday.com/content/images/articles/322/322736/elephant-from-the-front.jpg')),
Center(
child: ClipPath(
clipper: InvertedRect(),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
color: Colors.transparent,
),
),
),
),
],
),
),
);
}
}
class InvertedRect extends CustomClipper<Path> {
#override
getClip(Size size) {
print(size);
return Path()
..addRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height))
..addRect(Rect.fromLTWH(100.0, 40.0, 100, 100))
..fillType = PathFillType.evenOdd;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}

Related

Curved Container for the screen - flutter

I'm trying to create a curved container (semi circle) on whole screen using flutter
image for the curved
I tried to use some solutions found on stackoverflow but it didn't work.
can anyone help
thanks
Wrap your widget with stack and then provide clip oval to the container and positioned your container to the left with half of your container width.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Demo(),
);
}
}
class Demo extends StatelessWidget {
const Demo({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Positioned(
top: -10,
bottom: -10,
left: -200,
child: ClipOval(
child: Container(
height: double.maxFinite,
width: 400,
color: Colors.black.withOpacity(0.5),
),
),
),
],
),
);
}
}
You will get the following output

Flutter ClipRect and Align : how to compute the Alignment values

I want to display part of an image (a rectangle of 128 * 128 inside this image which is 512 * 512) in a rectangle in Flutter app.
Like this:
I want to select a specific zone in the source image, here from x=115, y=12 with width=128,height=128.
So here's my Flutter code to do this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
var myWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: myWidth,
height: myWidth,
color: Colors.yellow,
child: FittedBox(
fit: BoxFit.fill,
child: ClipRect(
child: Align(
alignment: Alignment(2.0*115.0 / 512.0 - 1, 2.0*12.0 / 512.0 -1.0),
widthFactor: 0.25,
heightFactor: 0.25,
child: Image.network(
"https://homepages.cae.wisc.edu/~ece533/images/baboon.png"),
),
),
)));
}
}
I suppose that the widthFactor and heightFactor represent the percentage of the original image I want to display - so here, because I want to display 128 pixels, it's 25% of the original width/height.
However, I just can't figure out out to calculate the values for the alignment.
First I tried for x :
alignment: Alignment(2.0*115.0 / 512.0 - 1, 2.0*12.0 / 512.0 -1.0),
Which almost gives the desired result, but not quite, see below:
I tried many other combinations to calculate the Alignment values and read all I could find with the help of Google... Is this even possible to accomplish this with a ClipRect and an Align?
Any help is welcome ;-)
NOTE : I don't want to crop the original image (and hence produce a new image), I want to keep it as downloaded to avoid having N copies of the same bitmap in memory. What I want is a specific view on the image - something that is absolutely trivial to do with OpenGL/DirectX and UV coordinates for example, but not so with Flutter.
Based on pskink comment, provided a working solution in the original post.
import 'package:flutter/material.dart';
import 'package:network_to_file_image/image_for_canvas.dart';
import 'package:network_to_file_image/network_to_file_image.dart';
import 'dart:ui' as ui;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var myWidth = MediaQuery.of(context).size.width;
User user = User(
filename: null,
url: "https://homepages.cae.wisc.edu/~ece533/images/baboon.png",
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: myWidth,
height: myWidth,
color: Colors.yellow,
child: CustomPaint(
painter: ImagePainter(user, loadCallback: (_, __, ___) {
setState(() {});
})),
));
}
}
class ImagePainter extends CustomPainter {
final User user;
final LoadCallback<User> loadCallback;
ImagePainter(
this.user, {
required this.loadCallback,
});
#override
void paint(Canvas canvas, Size size) {
canvas.save();
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..color = Colors.lightGreenAccent);
canvas.restore();
var imageForCanvas = _imageForCanvas();
ui.Image? image = imageForCanvas.image(user);
if (image != null) {
canvas.save();
canvas.drawImageRect(
image,
Rect.fromLTWH(
12,
12,
256,
256,
),
Rect.fromLTWH(
0,
0,
size.width,
size.height,
),
Paint()..imageFilter = ui.ImageFilter.blur(sigmaX: .5, sigmaY: .5),
);
canvas.restore();
}
}
ImageForCanvas<User> _imageForCanvas() => ImageForCanvas<User>(
imageProviderSupplier: (User user) => NetworkToFileImage(
file: null,
url: user.url,
),
keySupplier: (User user) => user.url!,
loadCallback: loadCallback,
);
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class User {
final String? filename;
final String? url;
User({
this.filename,
this.url,
});
}

Container inside ListView will expand even though it has height

The following Container inside a ListView, won't have a height, even though I specified one. Instead it expands to the entire ListView's height
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 Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: [
Container(
width: 100,
height: 20,
color: Colors.red)
])
);
}
}
According to https://api.flutter.dev/flutter/widgets/Container-class.html,
If the widget has no child and no alignment, but a height, width, or
constraints are provided, then the Container tries to be as small as
possible given the combination of those constraints and the parent's
constraints.
so shouldn't the Container be as small as possible, since it has constraints?
Have a look at Understanding Constraints in Flutter Docs.
The constraints given by the ListView to its children is too exactly fill the cross axis. Your Container cannot decide to have a size out of these constraints.
Remember, constraints go down, sizes go up.
You should position your Containers inside the ListView. In the following example, the first Container (Red) is not positioned, the second (Green) is Centered and the last (Blue) is Aligned on topLeft:
Center andAlign widgets both will take maximum height and give flexibility to their child, your Container.
Full source code:
import 'dart:math' show Random;
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 Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey.shade200,
child: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: [
Container(width: 100, height: 20, color: Colors.red),
Center(
child: Container(width: 100, height: 10, color: Colors.green),
),
Align(
alignment: Alignment.topLeft,
child: Container(width: 100, height: 20, color: Colors.blue),
),
],
),
),
);
}
}
Try to wrap the ListView with a Container with a fixed height.
The ListView will take up all available space unless constrained, constraining the height of the ListView would fix the issue. You can wrap the ListView in a container with certain height (20, in your case).

How do I make Scaffold.bottomSheet partly transparent?

Is there a way to make Scaffold.bottomSheet partly transparent (like for a notch that shows the body content behind it)? I noticed that even adding just Text implicitly puts a white background (Material?) below it, and I think this is not configurable.
class MyHomePage extends StatefulWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: // ...
),
bottomSheet: Text('This is a bottom sheet'),
);
}
}
FWIW, Scaffold.bottomNavigationBar supports being semi-transparent (e.g. you can have a notch around FAB).
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Demo Home Page'),
theme: ThemeData(
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: Colors.black.withOpacity(0.5)),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
bottomSheet: Container(
height: 40,
width: MediaQuery.of(context).size.width,
child: Center(child: Text('semi transparent bottom sheet :)', style: TextStyle(color: Colors.white),))
),
);
}
}
this was previously posted

Flare Flutter Animations

Animations created with Flare Flutter (from 2dimensions.com) cannot switch between different animations of the same Flare Actor. If a black version is first, the white version will not display; if the white version is first, the black will display.
I am not sure if I am doing something wrong or if it is a bug. It can switch between colors, just not animations.
import 'package:flutter/material.dart';
import 'package:flare_flutter/flare_actor.dart';
const List<String> animations = ['White', 'Black'];
const List<Color> colors = [Colors.blue, Colors.black];
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Animation Tester',
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Animation Tester'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int index = 0;
void switchAnimation() {
setState(() {
index = index < (animations.length - 1) ? index + 1 : 0;
});
}
#override
Widget build(BuildContext context) {
print(index);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView(
children: <Widget>[
GestureDetector(
onTap: switchAnimation,
child: Icon(
Icons.add,
size: 100.0,
)),
Container(
width: 200.0,
height: 200.0,
child: FlareActor(
'assets/color_wheel_loading.flr',
color: colors[index],
)),
Container(
width: 200.0,
height: 200.0,
child: FlareActor(
'assets/color_wheel_loading.flr',
animation: animations[index],
)),
Center(child: Text('$index'))
],
)),
);
}
}
I have tested your code with my own file, it works perfect. May be your animation names are not right, can you check.
Or you can test this file "https://www.2dimensions.com/a/whitewolfnegizzz/files/flare/pj" and using the code below.
import 'package:flutter/material.dart';
import 'package:flare_flutter/flare_actor.dart';
const List<String> animations = ['Build and Fade Out', 'Build'];
const List<Color> colors = [Colors.blue, Colors.black];
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Animation Tester',
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Animation Tester'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int index = 0;
void switchAnimation() {
setState(() {
index = index < (animations.length - 1) ? index + 1 : 0;
});
}
#override
Widget build(BuildContext context) {
print(index);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView(
children: <Widget>[
GestureDetector(
onTap: switchAnimation,
child: Icon(
Icons.add,
size: 100.0,
)),
Container(
width: 200.0,
height: 200.0,
child: FlareActor(
'assets/color_wheel_loading.flr',
color: colors[index],
)),
Container(
width: 200.0,
height: 200.0,
child: FlareActor(
'assets/Pj.flr',
animation: animations[index],
)),
Center(child: Text('$index'))
],
)),
);
}
}
From what i observed, Flare animation names are case sensitive. If the animation names in the flare project are in lowercase, the animation property of your flare actor should also be in lowercase.
Add flr in assets.
initialize it in Pubsepec.yaml file
then add dependency of flare animation in pubsepec.yaml file
after this,
just use this code in your main file
Container(
height: MediaQuery.of(context).size.height *0.8,
child: FlareActor(
'assets/oncemore.flr',
animation: 'Celebrate Duplicate', // Check this, when you are downloading flr file from Flare 2D dimension website
fit: BoxFit.contain,
),
),
After this , Your Flare animation will work perfectly.