passing string as FontAwesomeIcons - dart

am using font_awesome_flutter in my app to display some icons..
am getting the name of the icons from json as strings .. how i can pass it to the icon?
there is a way to achieve this?
for example:
am getting from the json:
String icon = 'ad';
and then i want to use it like this:
new Icon(FontAwesomeIcons.icon),
i know it doesn't work like this .. but how can i do this? is it doable?

I found one way may that help you. edit font_awesome_flutter.dart file as following and also access as below.
I just demonstrate with two Icon you can go on with as much as you need or for all.
font_awesome_flutter.dart
library font_awesome_flutter;
import 'package:flutter/widgets.dart';
import 'package:font_awesome_flutter/icon_data.dart';
// THIS FILE IS AUTOMATICALLY GENERATED!
class FontAwesomeIcons {
static const createDoc = {
'fiveHundredPx': IconDataBrands(0xf26e),
'accessibleIcon': IconDataBrands(0xf368),
//.......add all Icons HERE
};
static const IconData fiveHundredPx = const IconDataBrands(0xf26e);
static const IconData accessibleIcon = const IconDataBrands(0xf368);
static const IconData accusoft = const IconDataBrands(0xf369);
static const IconData acquisitionsIncorporated = const IconDataBrands(0xf6af);
static const IconData ad = const IconDataSolid(0xf641);
static const IconData addressBook = const IconDataRegular(0xf2b9);
static const IconData solidAddressBook = const IconDataSolid(0xf2b9);
static const IconData addressCard = const IconDataRegular(0xf2bb);
static const IconData solidAddressCard = const IconDataSolid(0xf2bb);
//.......
//.......add all Icons HERE To as already in your file
}
Now you can use as Following Code:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(new FontAwesomeGalleryApp());
}
class FontAwesomeGalleryApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Font Awesome Flutter Gallery',
theme: new ThemeData.light().copyWith(
iconTheme: new IconThemeData(size: 36.0, color: Colors.black87),
textTheme: new TextTheme(
body1: new TextStyle(fontSize: 16.0, color: Colors.black87),
),
),
home: new Home(),
);
}
}
class Home extends StatelessWidget {
String data = 'fiveHundredPx';
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Container(
child: Center(
child: Icon(FontAwesomeIcons.createDoc[data.toString()]),
),
),
);
}
}
I know That This Way is little bit tough but i found this way only.

You can save the unicode data in the Json. For example :
static const IconData clock = const IconDataRegular(0xf017);
save f017 in the Json.
Later use the following function for conversion into an int.
int getHexFromStr(String fontCode) {
int val = 0;
int len = fontCode.length;
for (int i = 0; i < len; i++) {
int hexDigit = fontCode.codeUnitAt(i);
if (hexDigit >= 48 && hexDigit <= 57) {
val += (hexDigit - 48) * (1 << (4 * (len - 1 - i)));
} else if (hexDigit >= 65 && hexDigit <= 70) {
// A..F
val += (hexDigit - 55) * (1 << (4 * (len - 1 - i)));
} else if (hexDigit >= 97 && hexDigit <= 102) {
// a..f
val += (hexDigit - 87) * (1 << (4 * (len - 1 - i)));
} else {
throw new FormatException("An error occurred when converting");
}
}
return val;
}
And finally use the following :
Icon(IconDataSolid(getHexFromStr(' f017')))

Related

dart:ffi library ERROR: Unused import: 'package:ffi/ffi.dart' while trying to use allocate() and free() methods

I am trying to make an app that captures image (and doesnt display the camera stream) using dart:ffi (.c file is not included in code below). (I used this page) I use allocate() and free() methods, but I get an error: "The method 'free' ('allocate') isn't defined for the type '_MyHomePageState'. How do i fix this?
calloc.allocate(), calloc.free(), malloc.allocate() and malloc.free() work and then ffi library is used. Without this, errors appear again.
I have ffi: ^2.0.1 in my dependencies and i ran flutter pub get, flutter clean, flutter outdated, flutter upgrade and restarted my computer.
import 'dart:io';
import 'dart:typed_data';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as imglib;
typedef convert_func = Pointer<Uint32> Function(Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>, Int32, Int32, Int32, Int32);
typedef Convert = Pointer<Uint32> Function(Pointer<Uint8>, Pointer<Uint8>, Pointer<Uint8>, int, int, int, int);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Camera App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyHomePage(title: 'Camera App'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late CameraController _camera;
bool _cameraInitialized = false;
late CameraImage _savedImage;
final DynamicLibrary convertImageLib = Platform.isAndroid
? DynamicLibrary.open("lib-convertImage.so")
: DynamicLibrary.process();
late Convert conv;
#override
void initState() {
super.initState();
_initializeCamera();
// Load the convertImage() function from the library
conv = convertImageLib.lookup<NativeFunction<convert_func>>('convertImage').asFunction<Convert>();
}
void _initializeCamera() async {
// Get list of cameras of the device
List<CameraDescription> cameras = await availableCameras();
// Create the CameraController
_camera = CameraController(cameras[0], ResolutionPreset.veryHigh);
_camera.initialize().then((_) async{
// Start ImageStream
await _camera.startImageStream((CameraImage image) => _processCameraImage(image));
setState(() {
_cameraInitialized = true;
});
});
}
void _processCameraImage(CameraImage image) async {
setState(() {
_savedImage = image;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: const Center(),
floatingActionButton: FloatingActionButton(
onPressed: (){
// Allocate memory for the 3 planes of the image
Pointer<Uint8> p = allocate(count: _savedImage.planes[0].bytes.length);
Pointer<Uint8> p1 = allocate(count: _savedImage.planes[1].bytes.length);
Pointer<Uint8> p2 = allocate(count: _savedImage.planes[2].bytes.length);
// Assign the planes data to the pointers of the image
Uint8List pointerList = p.asTypedList(_savedImage.planes[0].bytes.length);
Uint8List pointerList1 = p1.asTypedList(_savedImage.planes[1].bytes.length);
Uint8List pointerList2 = p2.asTypedList(_savedImage.planes[2].bytes.length);
pointerList.setRange(0, _savedImage.planes[0].bytes.length, _savedImage.planes[0].bytes);
pointerList1.setRange(0, _savedImage.planes[1].bytes.length, _savedImage.planes[1].bytes);
pointerList2.setRange(0, _savedImage.planes[2].bytes.length, _savedImage.planes[2].bytes);
// Call the convertImage function and convert the YUV to RGB
Pointer<Uint32> imgP = conv(p, p1, p2, _savedImage.planes[1].bytesPerRow,
_savedImage.planes[1].bytesPerPixel, _savedImage.width, _savedImage.height);
// Get the pointer of the data returned from the function to a List
List imgData = imgP.asTypedList((_savedImage.width * _savedImage.height));
// Generate image from the converted data
imglib.Image img = imglib.Image.fromBytes(_savedImage.height, _savedImage.width, imgData);
// Free the memory space allocated
// from the planes and the converted data
free(p);
free(p1);
free(p2);
free(imgP);
},
tooltip: 'Increment',
child: const Icon(Icons.camera_alt),
), // This trailing comma makes auto-formatting nicer for build methods.
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}

Scroll multiple pages in PageView easier

I am building a horizontally scrollable list in flutter using PageView, however, I would like to be able to scroll multiple pages at the same time. It is currently possible if I scroll really fast, but it is far from ideal. Another option is to set pageSnapping to false, but then it does not snap at all, which is not what I want.
I am thinking that it might be possible to change pageSnapping from false to true if the scroll velocity is under a certain treshhold, but i don't know how I would get said velocity.
The app i am building looks something like this.
All help appreciated!
To anyone coming here in the future, i finally solved this using a Listener insted of a GestureDetector to calculate the code manually.
Here is the relevant code:
class HomeWidget extends StatefulWidget {
#override
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
int _currentPage = 0;
PageController _pageController;
int t; //Tid
double p; //Position
#override
initState() {
super.initState();
_pageController = PageController(
viewportFraction: 0.75,
initialPage: 0,
);
}
#override
Widget build(BuildContext context) {
return Container(
child: Listener(
onPointerMove: (pos) { //Get pointer position when pointer moves
//If time since last scroll is undefined or over 100 milliseconds
if (t == null || DateTime.now().millisecondsSinceEpoch - t > 100) {
t = DateTime.now().millisecondsSinceEpoch;
p = pos.position.dx; //x position
} else {
//Calculate velocity
double v = (p - pos.position.dx) / (DateTime.now().millisecondsSinceEpoch - t);
if (v < -2 || v > 2) { //Don't run if velocity is to low
//Move to page based on velocity (increase velocity multiplier to scroll further)
_pageController.animateToPage(_currentPage + (v * 1.2).round(),
duration: Duration(milliseconds: 800), curve: Curves.easeOutCubic);
}
}
},
child: PageView(
controller: _pageController,
physics: ClampingScrollPhysics(), //Will scroll to far with BouncingScrollPhysics
scrollDirection: Axis.horizontal,
children: <Widget>[
//Pages
],
),
),
);
}
}
Interesting problem!
To gain the velocity of a swipe you can use a GestureDetector, unfortunately when trying to use both a GestureDetector and PageView then the PageView steals the focus from the GestureDetector so they can not be used in unison.
GestureDetector(
onPanEnd: (details) {
Velocity velocity = details.velocity;
print("onPanEnd - velocity: $velocity");
},
)
Another way however was to use DateTime in the PageView's onPageChanged to measure the change in time, instead of velocity. However this is not ideal, the code I wrote is like a hack. It has a bug (or feature) that the first swipe after a standstill on a page will only move one page, however consecutive swipes will be able to move multiple pages.
bool pageSnapping = true;
List<int> intervals = [330, 800, 1200, 1600]; // Could probably be optimised better
DateTime t0;
Widget timeBasedPageView(){
return PageView(
onPageChanged: (item) {
// Obtain a measure of change in time.
DateTime t1 = t0 ?? DateTime.now();
t0 = DateTime.now();
int millisSincePageChange = t0.difference(t1).inMilliseconds;
print("Millis: $millisSincePageChange");
// Loop through the intervals, they only affect how much time is
// allocated before pageSnapping is enabled again.
for (int i = 1; i < intervals.length; i++) {
bool lwrBnd = millisSincePageChange > intervals[i - 1];
bool uprBnd = millisSincePageChange < intervals[i];
bool withinBounds = lwrBnd && uprBnd;
if (withinBounds) {
print("Index triggered: $i , lwr: $lwrBnd, upr: $uprBnd");
// The two setState calls ensures that pageSnapping will
// always return to being true.
setState(() {
pageSnapping = false;
});
// Allows some time for the fast pageChanges to proceed
// without being pageSnapped.
Future.delayed(Duration(milliseconds: i * 100)).then((val){
setState(() {
pageSnapping = true;
});
});
}
}
},
pageSnapping: pageSnapping,
children: widgets,
);
}
I hope this helps in some way.
Edit: another answer based upon Hannes' answer.
class PageCarousel extends StatefulWidget {
#override
_PageCarouselState createState() => _PageCarouselState();
}
class _PageCarouselState extends State<PageCarousel> {
int _currentPage = 0;
PageController _pageController;
int timePrev; //Tid
double posPrev; //Position
List<Widget> widgets = List.generate(
10,
(item) =>
Container(
padding: EdgeInsets.all(8),
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Index $item"),
],
),
),
));
#override
void initState() {
super.initState();
_pageController = PageController(
viewportFraction: 0.75,
initialPage: 0,
);
}
int boundedPage(int newPage){
if(newPage < 0){
return 0;
}
if(newPage >= widgets.length){
return widgets.length - 1;
}
return newPage;
}
#override
Widget build(BuildContext context) {
return Container(
child: Listener(
onPointerDown: (pos){
posPrev = pos.position.dx;
timePrev = DateTime.now().millisecondsSinceEpoch;
print("Down");
print("Time: $timePrev");
},
onPointerUp: (pos){
int newTime = DateTime.now().millisecondsSinceEpoch;
int timeDx = newTime - timePrev;
double v = (posPrev - pos.position.dx) / (timeDx);
int newPage = _currentPage + (v * 1.3).round();
print("Velocity: $v");
print("New Page: $newPage, Old Page: $_currentPage");
if (v < 0 && newPage < _currentPage || v >= 0 && newPage > _currentPage) {
_currentPage = boundedPage(newPage);
}
_pageController.animateToPage(_currentPage,
duration: Duration(milliseconds: 800), curve: Curves.easeOutCubic);
},
child: PageView(
controller: _pageController,
physics: ClampingScrollPhysics(), //Will scroll to far with BouncingScrollPhysics
scrollDirection: Axis.horizontal,
children: widgets,
),
),
);
}
}
This should ensure a decent multi page navigation.
The easiest and most natural way is to customize the physics of the PageView. You can set velocityPerOverscroll (logical pixels per second) according to your needs.
PageView(
pageSnapping: false,
physics: const PageOverscrollPhysics(velocityPerOverscroll: 1000),
Extend ScrollPhysics to control the snap behavior:
class PageOverscrollPhysics extends ScrollPhysics {
///The logical pixels per second until a page is overscrolled.
///A satisfying value can be determined by experimentation.
///
///Example:
///If the user scroll velocity is 3500 pixel/second and [velocityPerOverscroll]=
///1000, then 3.5 pages will be overscrolled/skipped.
final double velocityPerOverscroll;
const PageOverscrollPhysics({
ScrollPhysics? parent,
this.velocityPerOverscroll = 1000,
}) : super(parent: parent);
#override
PageOverscrollPhysics applyTo(ScrollPhysics? ancestor) {
return PageOverscrollPhysics(
parent: buildParent(ancestor)!,
);
}
double _getTargetPixels(ScrollMetrics position, double velocity) {
double page = position.pixels / position.viewportDimension;
page += velocity / velocityPerOverscroll;
double pixels = page.roundToDouble() * position.viewportDimension;
return pixels;
}
#override
Simulation? createBallisticSimulation(
ScrollMetrics position, double velocity) {
// If we're out of range and not headed back in range, defer to the parent
// ballistics, which should put us back in range at a page boundary.
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
return super.createBallisticSimulation(position, velocity);
}
final double target = _getTargetPixels(position, velocity);
if (target != position.pixels) {
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
}
return null;
}
#override
bool get allowImplicitScrolling => false;
}
Note that it is also required to set pageSnapping: false, this is done intentionally to disable the internal physics of PageView, which we are overwriting with PageOverscrollPhysics. Although PageOverscrollPhysics might look crazy complicated, it's essentially just an adjustment of the PageScrollPhysics()class.

Is it possible to rotate pie charts with the charts_flutter package?

Is it possible to have this same effect using the package charts_flutter? In this case the user can rotate the pie chart.
User rotating pie chart
It's not possible with the current implementation of the charting library you're using unless you used their code and changed. You might be able to get it to work with the flutter circular chart plugin by hooking up your gesture detecting code and animating the value for startAngle, but I'm not sure it would do exactly what you want (or might try to redraw the entire thing each time which isn't overly performant).
I had some old code lying around that implemented most of what you want so I fixed it up a little - here's an example of just writing your own pie chart. You can copy/paste it into a file and run it as-is.
Your mileage may vary with this - I haven't tested it extensively or anything, but you're welcome to use it at least as a starting point - it has the code for drawing a pie chart and rotating according to gestures at least.
There's quite a lot of stuff in here so I'd encourage you to give it a deep read-through to see exactly what I'm doing. I don't have time to add documentation right now, but if you have any questions feel free to ask.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Material(
child: RotatingPieChart(
items: [
PieChartItem(30, "one", Colors.red),
PieChartItem(210, "two", Colors.green),
PieChartItem(60, "three", Colors.blue),
PieChartItem(35, "four", Colors.teal),
PieChartItem(25, "five", Colors.orange)
],
toText: (item, _) => TextPainter(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(color: Colors.black, fontSize: 8.0),
text: "${item.name}\n${item.val}",
),
textDirection: TextDirection.ltr),
),
),
),
);
}
}
class PieChartItem {
final num val;
final String name;
final Color color;
PieChartItem(this.val, this.name, this.color) : assert(val != 0);
}
typedef TextPainter PieChartItemToText(PieChartItem item, double total);
class RotatingPieChart extends StatelessWidget {
final double accellerationFactor;
final List<PieChartItem> items;
final PieChartItemToText toText;
const RotatingPieChart({Key key, this.accellerationFactor = 1.0, #required this.items, #required this.toText})
: super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: AspectRatio(
aspectRatio: 1.0,
child: _RotatingPieChartInternal(
items: items,
toText: toText,
accellerationFactor: accellerationFactor,
),
),
);
}
}
class _RotationEndSimulation extends Simulation {
final double initialVelocity;
final double initialPosition;
final double accelleration;
_RotationEndSimulation({
#required this.initialVelocity,
#required double decelleration,
#required this.initialPosition,
}) : accelleration = decelleration * -1.0;
#override
double dx(double time) => initialVelocity + (accelleration * time);
#override
bool isDone(double time) => initialVelocity > 0 ? dx(time) < 0.001 : dx(time) > -0.001;
#override
double x(double time) => (initialPosition + (initialVelocity * time) + (accelleration * time * time / 2)) % 1.0;
}
class _RotatingPieChartInternal extends StatefulWidget {
final double accellerationFactor;
final List<PieChartItem> items;
final PieChartItemToText toText;
const _RotatingPieChartInternal(
{Key key, this.accellerationFactor = 1.0, #required this.items, #required this.toText})
: super(key: key);
#override
_RotatingPieChartInternalState createState() => _RotatingPieChartInternalState();
}
class _RotatingPieChartInternalState extends State<_RotatingPieChartInternal> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
#override
void initState() {
_controller = AnimationController(vsync: this);
_animation = new Tween(begin: 0.0, end: 2.0 * pi).animate(_controller);
_controller.animateTo(2 * pi, duration: Duration(seconds: 10));
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
Offset lastDirection;
Offset getDirection(Offset globalPosition) {
RenderBox box = context.findRenderObject();
Offset offset = box.globalToLocal(globalPosition);
Offset center = Offset(context.size.width / 2.0, context.size.height / 2.0);
return offset - center;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: (details) {
lastDirection = getDirection(details.globalPosition);
},
onPanUpdate: (details) {
Offset newDirection = getDirection(details.globalPosition);
double diff = newDirection.direction - lastDirection.direction;
var value = _controller.value + (diff / pi / 2);
_controller.value = value % 1.0;
lastDirection = newDirection;
},
onPanEnd: (details) {
// non-angular velocity
Offset velocity = details.velocity.pixelsPerSecond;
var top = (lastDirection.dx * velocity.dy) - (lastDirection.dy * velocity.dx);
var bottom = (lastDirection.dx * lastDirection.dx) + (lastDirection.dy * lastDirection.dy);
var angularVelocity = top / bottom;
var angularRotation = angularVelocity / pi / 2;
var decelleration = angularRotation * widget.accellerationFactor;
_controller.animateWith(
_RotationEndSimulation(
decelleration: decelleration,
initialPosition: _controller.value,
initialVelocity: angularRotation,
),
);
},
child: AnimatedBuilder(
animation: _animation,
builder: (context, widget) {
return Stack(
fit: StackFit.passthrough,
children: [
Transform.rotate(
angle: _animation.value,
child: widget,
),
CustomPaint(
painter:
_PieTextPainter(items: this.widget.items, rotation: _animation.value, toText: this.widget.toText),
)
],
);
},
child: CustomPaint(
painter: _PieChartPainter(
items: widget.items,
),
),
),
);
}
}
abstract class _AlignedCustomPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// for convenience I'm doing all the drawing in a 100x100 square then moving it rather than worrying
// about the actual size.
// Also, using a 100x100 square for convenience so we can hardcode values.
FittedSizes fittedSizes = applyBoxFit(BoxFit.contain, Size(100.0, 100.0), size);
var dest = fittedSizes.destination;
canvas.translate((size.width - dest.width) / 2 + 1, (size.height - dest.height) / 2 + 1);
canvas.scale((dest.width - 2) / 100.0);
alignedPaint(canvas, Size(100.0, 100.0));
}
void alignedPaint(Canvas canvas, Size size);
}
class _PieChartPainter extends _AlignedCustomPainter {
final List<PieChartItem> items;
final double total;
final double rotation;
_PieChartPainter({this.rotation = 0.0, #required this.items})
: total = items.fold(0.0, (total, el) => total + el.val);
#override
void alignedPaint(Canvas canvas, Size size) {
Rect rect = Offset.zero & size;
double soFar = rotation;
Paint outlinePaint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke;
for (int i = 0; i < items.length; ++i) {
PieChartItem item = items[i];
double arcRad = item.val / total * 2 * pi;
canvas.drawArc(rect, soFar, arcRad, true, Paint()..color = item.color);
canvas.drawArc(rect, soFar, arcRad, true, outlinePaint);
soFar += arcRad;
}
}
#override
bool shouldRepaint(_PieChartPainter oldDelegate) {
return oldDelegate.rotation != rotation || oldDelegate.items != items;
}
}
class _PieTextPainter extends _AlignedCustomPainter {
final List<PieChartItem> items;
final double total;
final double rotation;
final List<double> middles;
final PieChartItemToText toText;
static final double textDisplayCenter = 0.7;
_PieTextPainter._(this.items, this.total, this.rotation, this.middles, this.toText);
factory _PieTextPainter(
{double rotation = 0.0, #required List<PieChartItem> items, #required PieChartItemToText toText}) {
double total = items.fold(0.0, (prev, el) => prev + el.val);
var middles = (() {
double soFar = rotation;
return items.map((item) {
double arcRad = item.val / total * 2 * pi;
double middleRad = (soFar) + (arcRad / 2);
soFar += arcRad;
return middleRad;
}).toList(growable: false);
})();
return _PieTextPainter._(items, total, rotation, middles, toText);
}
#override
void alignedPaint(Canvas canvas, Size size) {
for (int i = 0; i < items.length; ++i) {
var middleRad = middles[i];
var item = items[i];
var rad = size.width / 2;
var middleX = rad + rad * textDisplayCenter * cos(middleRad);
var middleY = rad + rad * textDisplayCenter * sin(middleRad);
TextPainter textPainter = toText(item, total)..layout();
textPainter.paint(canvas, Offset(middleX - (textPainter.width / 2), middleY - (textPainter.height / 2)));
}
}
#override
bool shouldRepaint(_PieTextPainter oldDelegate) {
// note that just checking items != items might not be enough.
return oldDelegate.rotation != rotation || oldDelegate.items != items || oldDelegate.toText != toText;
}
}

How can I customize / rotate a BoxDecoration for a Container widget in Flutter?

I have a Widget that builds a circular profile picture for bus stops, and as of right now it has a circular border surrounding the profile picture like such. I want to change the circular border to be dashed instead and to also animate by circling/rotating around the profile picture in its dashed form. Is there any easy way to do that? Thanks so much for any help you can provide!
return new Transform(
transform: new Matrix4.diagonal3Values(
animation.avatarSize.value,
animation.avatarSize.value,
1.0,
),
alignment: Alignment.center,
child: new Container(
width: 110.0,
height: 110.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
border: new Border.all(
color: Colors.white30,
),
),
margin: const EdgeInsets.only(
top: 24.0,
left: 16.0,
),
padding: const EdgeInsets.all(3.0),
child: new ClipOval(
child: new Image.asset(
stopName.avatar,
),
),
),
);
Unfortunately the simple answer is that there is no easy way to do that. The flutter people in their infinite wisdom have made the conclusion that dashed lines are not performant enough to be included in flutter and therefore no-one will ever need to draw a dashed line. (Yes, the logical discontinuity in that sentence is intended. Don't get me wrong - I love flutter and the devs have done a great job, but they do seem to have made a few semi-arbitrary decisions based on performance rather than functionality).
The interpretation I've seen of why is that basically the C++ version would be doing something similar to dart code (unless it were done directly on the GPU), and so they expect someone to eventually implement dashed lines in dart instead (possibly as part of a library?). See this github bug for progress and discussion... and +1 it if you want to see dashes in future. If enough people do that then eventually the flutter people may decide to implement it.
But for now, that means that there is no easy way to make CircleBorder, or any other type of border, with a dashed line.
However, with maximum effort, exactly what you want can be achieved =). Below is a quick cut of the code for you that does what you want. Be warned - it isn't very optimized, and there are probably easier ways to do it, and you could probably use a Decoration and implement the paint etc there or something... but this does work.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new SafeArea(
child: Column(
children: [
new DashedCircle(
child: new ClippedDrawing(),
)
],
),
),
);
}
}
class ClippedDrawing extends StatelessWidget {
#override
Widget build(BuildContext context) => new ClipOval(
child: new Container(
color: Colors.red,
),
);
}
class DashedCircle extends StatefulWidget {
final Widget child;
const DashedCircle({Key key, this.child}) : super(key: key);
#override
DashedBorderState createState() => new DashedBorderState();
}
class DashedBorderState extends State<DashedCircle> with TickerProviderStateMixin<DashedCircle> {
AnimationController controller;
Animation<double> animation;
#override
void initState() {
super.initState();
controller = new AnimationController(vsync: this, duration: Duration(seconds: 10));
animation = new Tween(begin: 0.0, end: pi * 2.0).animate(controller);
controller.repeat();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return new CustomPaint(
painter: OvalPainter(
color: Colors.blue, borderWidth: 1.0, dashLength: 5.0, spaceLength: 2.0, offset: animation.value),
child: child,
);
},
child: Container(
width: 110.0,
height: 110.0,
padding: EdgeInsets.all(3.0),
child: widget.child,
),
);
}
}
class OvalPainter extends CustomPainter {
final Color color;
final double borderWidth;
final double dashLength;
final double spaceLength;
final double offset;
OvalPainter(
{#required this.borderWidth,
#required this.dashLength,
#required this.spaceLength,
#required this.offset,
#required this.color});
double lastShortestSide;
double lastDashLength;
double lastSpaceLength;
Path lastPath;
#override
void paint(Canvas canvas, Size size) {
Rect rect = Offset.zero & size;
var radius = rect.shortestSide / 2;
canvas.translate(radius, radius);
canvas.rotate(offset);
Path path;
if (lastShortestSide == rect.shortestSide &&
dashLength == lastDashLength &&
spaceLength == lastSpaceLength &&
lastPath != null) {
path = lastPath;
} else {
path = _getDashedCircularPath(rect.shortestSide / 2, dashLength, spaceLength);
lastPath = path;
lastShortestSide = rect.shortestSide;
lastDashLength = dashLength;
lastSpaceLength = spaceLength;
}
canvas.drawPath(
path,
new Paint()
..style = PaintingStyle.stroke
..color = color
..strokeWidth = borderWidth,
);
}
#override
bool shouldRepaint(OvalPainter oldDelegate) {
return offset != oldDelegate.offset ||
color != oldDelegate.color ||
borderWidth != oldDelegate.borderWidth ||
dashLength != oldDelegate.dashLength ||
spaceLength != oldDelegate.spaceLength;
}
static Path _getDashedCircularPathFromNumber(double radius, int numSections, double dashPercentage) {
var tau = 2 * pi;
var actualTotalLength = tau / numSections;
var actualDashLength = actualTotalLength * dashPercentage;
double offset = 0.0;
Rect rect = new Rect.fromCircle(center: Offset.zero, radius: radius);
Path path = new Path();
for (int i = 0; i < numSections; ++i) {
path.arcTo(rect, offset, actualDashLength, true);
offset += actualTotalLength;
}
return path;
}
static Path _getDashedCircularPath(double radius, double dashLength, double spaceLength) {
// first, find number of radians that dashlength + spacelength take
var tau = 2 * pi;
var circumference = radius * tau;
var dashSpaceLength = dashLength + spaceLength;
var num = circumference / (dashSpaceLength);
// we'll floor the value so that it's close-ish to the same amount as requested but
// instead will be the same all around
var closeNum = num.floor();
return _getDashedCircularPathFromNumber(radius, closeNum, dashLength / dashSpaceLength);
}
}

How to use Pageable lazy list

I want to create a very long list of widgets that is user might want to scroll through, like a book.
I noticed PageableLazyList class, but couldn't quite understand how should I use it.
class NotePage extends PageableLazyList {
NotePage({Key key, this.note}) : super(key: key,itemBuilder: itembuilder);
final Note note;
static List<NoteContainer> itembuilder (BuildContext context, num start, num count ){
List<NoteContainer> result = <NoteContainer>[];
for(num i = start; i < start+count; i++){
result.add(new NoteContainer(new Note(title: "Note " + i.toString(), description: "Description " + i.toString())));
}
return result;
}
}
didn't work! I need a build method here, but couldn't understand what I am supposed to do.
Well this actually works:
class NotePageList extends PageableLazyList {
NotePageList({Key key}) : super(key: key,
scrollDirection: Axis.horizontal,
itemCount : 10000,
itemBuilder: itembuilder){
}
static List<NoteContainer> itembuilder (BuildContext context, num start, num count ){
List<NoteContainer> result = <NoteContainer>[];
for(num i = start; i < start+count; i++){
result.add(new NoteContainer(new Note(title: "Note " + i.toString(), description: "Description " + i.toString())));
}
return result;
}
}

Resources