Last active
February 19, 2021 15:07
-
-
Save collinjackson/49c77ab2f68275e68f12e481c52eefd0 to your computer and use it in GitHub Desktop.
ColorFilter example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:ui' as ui; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
/// Applies a color filter before painting its child. | |
class ColorFiltered extends SingleChildRenderObjectWidget { | |
/// Creates a widget that color filters its child. | |
/// | |
/// The [colorFilter] argument must not be null. | |
const ColorFiltered({ | |
Key key, | |
@required this.colorFilter, | |
Widget child, | |
}) : assert(colorFilter != null), | |
super(key: key, child: child); | |
/// The color filter to apply to the existing painted content when painting the child. | |
final ColorFilter colorFilter; | |
@override | |
RenderColorFiltered createRenderObject(BuildContext context) { | |
return RenderColorFiltered(colorFilter: colorFilter); | |
} | |
@override | |
void updateRenderObject(BuildContext context, RenderColorFiltered renderObject) { | |
renderObject.colorFilter = colorFilter; | |
} | |
} | |
/// A composited layer that applies a color filter to its children. | |
class ColorFilterLayer extends ContainerLayer { | |
/// Creates a color filter layer. | |
ColorFilterLayer({ | |
@required ColorFilter colorFilter, | |
}) : _colorFilter = colorFilter; | |
/// The color filter to apply to the children. | |
/// | |
/// The scene must be explicitly recomposited after this property is changed | |
/// (as described at [Layer]). | |
ColorFilter get colorFilter => _colorFilter; | |
ColorFilter _colorFilter; | |
set colorFilter(ColorFilter value) { | |
if (value != _colorFilter) { | |
_colorFilter = value; | |
markNeedsAddToScene(); | |
} | |
} | |
@override | |
ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) { | |
// TODO(jackson): We should use our colorFilter here. | |
// Unfortunately SceneBuilder doesn't support ColorFilter.matrix, only | |
// ColorFilters with a color and blend mode are supported. An engine | |
// update is required to support arbitrary color filters. | |
builder.pushColorFilter(Colors.red.withOpacity(0.5), BlendMode.difference); | |
addChildrenToScene(builder, layerOffset); | |
builder.pop(); | |
return null; // this does not return an engine layer yet. | |
} | |
@override | |
void debugFillProperties(DiagnosticPropertiesBuilder properties) { | |
super.debugFillProperties(properties); | |
properties.add(DiagnosticsProperty<ColorFilter>('colorFilter', colorFilter)); | |
} | |
} | |
/// Applies a color filter to the existing painted content and then paints [child]. | |
class RenderColorFiltered extends RenderProxyBox { | |
/// Creates a color filter. | |
/// | |
/// The [colorFilter] argument must not be null. | |
RenderColorFiltered({ RenderBox child, @required ColorFilter colorFilter }) | |
: assert(colorFilter != null), | |
_colorFilter = colorFilter, | |
super(child); | |
/// The color filter to apply to the existing painted content before painting | |
/// the child. | |
ColorFilter get colorFilter => _colorFilter; | |
ColorFilter _colorFilter; | |
set colorFilter(ColorFilter value) { | |
assert(value != null); | |
if (_colorFilter == value) | |
return; | |
_colorFilter = value; | |
markNeedsPaint(); | |
} | |
@override | |
bool get alwaysNeedsCompositing => child != null; | |
@override | |
void paint(PaintingContext context, Offset offset) { | |
if (child != null) { | |
assert(needsCompositing); | |
context.pushLayer(ColorFilterLayer(colorFilter: _colorFilter), super.paint, offset); | |
} | |
} | |
} | |
void main() => runApp(MyApp()); | |
// This is the standard "flutter create" sample app with a ColorFiltered added | |
class MyApp extends StatelessWidget { | |
// This widget is the root of your application. | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
// This is the theme of your application. | |
// | |
// Try running your application with "flutter run". You'll see the | |
// application has a blue toolbar. Then, without quitting the app, try | |
// changing the primarySwatch below to Colors.green and then invoke | |
// "hot reload" (press "r" in the console where you ran "flutter run", | |
// or simply save your changes to "hot reload" in a Flutter IDE). | |
// Notice that the counter didn't reset back to zero; the application | |
// is not restarted. | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
// This widget is the home page of your application. It is stateful, meaning | |
// that it has a State object (defined below) that contains fields that affect | |
// how it looks. | |
// This class is the configuration for the state. It holds the values (in this | |
// case the title) provided by the parent (in this case the App widget) and | |
// used by the build method of the State. Fields in a Widget subclass are | |
// always marked "final". | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
int _counter = 0; | |
void _incrementCounter() { | |
setState(() { | |
// This call to setState tells the Flutter framework that something has | |
// changed in this State, which causes it to rerun the build method below | |
// so that the display can reflect the updated values. If we changed | |
// _counter without calling setState(), then the build method would not be | |
// called again, and so nothing would appear to happen. | |
_counter++; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
// This method is rerun every time setState is called, for instance as done | |
// by the _incrementCounter method above. | |
// | |
// The Flutter framework has been optimized to make rerunning build methods | |
// fast, so that you can just rebuild anything that needs updating rather | |
// than having to individually change instances of widgets. | |
return ColorFiltered( | |
colorFilter: ColorFilter.mode( | |
Colors.red.withOpacity(0.5), | |
BlendMode.values[_counter % BlendMode.values.length], | |
), | |
child: | |
Scaffold( | |
appBar: AppBar( | |
// Here we take the value from the MyHomePage object that was created by | |
// the App.build method, and use it to set our appbar title. | |
title: Text(widget.title), | |
), | |
body: Center( | |
// Center is a layout widget. It takes a single child and positions it | |
// in the middle of the parent. | |
child: Column( | |
// Column is also layout widget. It takes a list of children and | |
// arranges them vertically. By default, it sizes itself to fit its | |
// children horizontally, and tries to be as tall as its parent. | |
// | |
// Invoke "debug painting" (press "p" in the console, choose the | |
// "Toggle Debug Paint" action from the Flutter Inspector in Android | |
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code) | |
// to see the wireframe for each widget. | |
// | |
// Column has various properties to control how it sizes itself and | |
// how it positions its children. Here we use mainAxisAlignment to | |
// center the children vertically; the main axis here is the vertical | |
// axis because Columns are vertical (the cross axis would be | |
// horizontal). | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
Text( | |
'You have pushed the button this many times:', | |
), | |
Text( | |
'$_counter', | |
style: Theme.of(context).textTheme.display1, | |
), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: _incrementCounter, | |
tooltip: 'Increment', | |
child: Icon(Icons.add), | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment