Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Last active February 15, 2021 07:22
Show Gist options
  • Save PlugFox/ffe83a91ce50f9c78a5b1d6674e36d1b to your computer and use it in GitHub Desktop.
Save PlugFox/ffe83a91ce50f9c78a5b1d6674e36d1b to your computer and use it in GitHub Desktop.
Flutter Web simple file upload
import 'package:flutter_web/material.dart';
import 'dart:math' as math;
class DottedBorder extends StatelessWidget {
final Color color;
final double strokeWidth;
final double gap;
final Widget child;
final EdgeInsets padding;
DottedBorder({
this.color = Colors.black,
this.strokeWidth = 1.0,
this.gap = 5.0,
this.padding = const EdgeInsets.all(8.0),
@required this.child,
}) {
assert(child != null);
}
@override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: EdgeInsets.all(strokeWidth / 2),
child: Stack(
children: <Widget>[
Positioned.fill(
child: CustomPaint(
painter: _DashRectPainter(
color: color,
strokeWidth: strokeWidth,
gap: gap,
),
),
),
Padding(
padding: padding,
child: child,
),
],
),
),
);
}
}
class _DashRectPainter extends CustomPainter {
double strokeWidth;
Color color;
double gap;
_DashRectPainter({
this.strokeWidth = 5.0,
this.color = Colors.black,
this.gap = 5.0,
});
@override
void paint(Canvas canvas, Size size) {
Paint dashedPaint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
double x = size.width;
double y = size.height;
Path _topPath = getDashedPath(
a: math.Point(0, 0),
b: math.Point(x, 0),
gap: gap,
);
Path _rightPath = getDashedPath(
a: math.Point(x, 0),
b: math.Point(x, y),
gap: gap,
);
Path _bottomPath = getDashedPath(
a: math.Point(0, y),
b: math.Point(x, y),
gap: gap,
);
Path _leftPath = getDashedPath(
a: math.Point(0, 0),
b: math.Point(0.001, y),
gap: gap,
);
canvas.drawPath(_topPath, dashedPaint);
canvas.drawPath(_rightPath, dashedPaint);
canvas.drawPath(_bottomPath, dashedPaint);
canvas.drawPath(_leftPath, dashedPaint);
}
Path getDashedPath({
@required math.Point<double> a,
@required math.Point<double> b,
@required gap,
}) {
Size size = Size(b.x - a.x, b.y - a.y);
Path path = Path();
path.moveTo(a.x, a.y);
bool shouldDraw = true;
math.Point currentPoint = math.Point(a.x, a.y);
num radians = math.atan(size.height / size.width);
num dx = math.cos(radians) * gap < 0
? math.cos(radians) * gap * -1
: math.cos(radians) * gap;
num dy = math.sin(radians) * gap < 0
? math.sin(radians) * gap * -1
: math.sin(radians) * gap;
while (currentPoint.x <= b.x && currentPoint.y <= b.y) {
shouldDraw
? path.lineTo(currentPoint.x, currentPoint.y)
: path.moveTo(currentPoint.x, currentPoint.y);
shouldDraw = !shouldDraw;
currentPoint = math.Point(
currentPoint.x + dx,
currentPoint.y + dy,
);
}
return path;
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
import 'package:flutter_web/cupertino.dart';
import 'package:flutter_web/material.dart';
import 'dart:async';
import 'dart:html';
import './dottedBorder.dart';
class DropzoneWidget extends StatefulWidget {
@override
_DropzoneWidgetState createState() => new _DropzoneWidgetState();
}
class _DropzoneWidgetState extends State<DropzoneWidget> {
StreamSubscription<MouseEvent> _onDragOverSubscription;
StreamSubscription<MouseEvent> _onDropSubscription;
StreamSubscription<MouseEvent> _onDragLeaveSubscription;
StreamSubscription<Event> _fileSelectionSubscription;
final StreamController<_DragState> _dragStateStreamController = new StreamController<_DragState>.broadcast();
final StreamController<Point<double>> _pointStreamController = new StreamController<Point<double>>.broadcast();
//FileUploadInputElement _inputElement;
List<File> _files = <File>[];
/*
String get _dropZoneText => this._files.isEmpty
? 'DropZONE'
: 'DropZONE\n(${this._files.length})'; //this._files.map<String>((File file) => file.name).join('\n');
*/
void _onDragOver(MouseEvent value) {
value.stopPropagation();
value.preventDefault();
this._pointStreamController.sink.add(Point<double>(value.layer.x.toDouble(), value.layer.y.toDouble()));
this._dragStateStreamController.sink.add(_DragState.dragging);
}
void _onDrop(MouseEvent value) {
value.stopPropagation();
value.preventDefault();
_pointStreamController.sink.add(null);
_addFiles(value.dataTransfer.files);
}
void _onDragLeave(MouseEvent value) {
this._dragStateStreamController.sink.add(_DragState.notDragging);
}
void _fileSelection(Event value) {
print(value);
//_addFiles((value.target as FileUploadInputElement).files);
}
void _addFiles(List<File> newFiles) {
this.setState(() {
this._files = this._files..addAll(newFiles);
print(this._files);
});
}
@override
void initState() {
super.initState();
this._onDragOverSubscription = document.body.onDragOver.listen(_onDragOver);
this._onDropSubscription = document.body.onDrop.listen(_onDrop);
this._onDragLeaveSubscription = document.body.onDragLeave.listen(_onDragLeave);
//this._inputElement = FileUploadInputElement();//..style.display = 'none';
//this._fileSelectionSubscription = this._inputElement.onChange.listen(_fileSelection);
}
@override
void dispose() {
this._onDropSubscription.cancel();
this._onDragOverSubscription.cancel();
this._onDragLeaveSubscription.cancel();
this._fileSelectionSubscription.cancel();
this._dragStateStreamController.close();
this._pointStreamController.close();
super.dispose();
}
@override
Widget build(BuildContext context)
=> LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints)
=> Stack(
children: <Widget>[
AnimatedContainer(
curve: Curves.linear,
duration: Duration(seconds: 1),
width: double.infinity,
height: double.infinity,
color: this._files.isEmpty
? const Color(0xFF81d4fa)
: const Color(0xFFff80ab),
child:Padding(
padding: const EdgeInsets.all(45),
child: DottedBorder(
color: const Color(0xFF263238),
strokeWidth: 5.0,
gap: 24.0,
child: Center(
child: Text(
'DropZONE',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: boxConstraints.maxWidth/20,
fontWeight: FontWeight.bold,
color: const Color(0xFF37474f),
),
),
),
),
),
),
Positioned(
top: 6,
right: 6,
width: 140,
height: 140,
child: IconButton(
alignment: Alignment.center,
icon: Icon(Icons.settings,),
color: const Color(0xFF37474f),
iconSize: 120,
tooltip: 'Settings',
onPressed: () async {
//this._inputElement.click();
await FileUploadInputElement().onChange.first;
print('settings');
},
),
),
StreamBuilder(
initialData: null,
stream: this._pointStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<Point<double>> snapPoint)
=> (snapPoint.data == null || snapPoint.data is! Point<double> || snapPoint.data == const Point<double>(0.0, 0.0))
? Container()
: StreamBuilder(
initialData: null,
stream: this._dragStateStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<_DragState> snapState)
=> (snapState.data == null || snapState.data is! _DragState || snapState.data == _DragState.notDragging)
? Container()
: Positioned(
height: 140,
width: 140,
left: snapPoint.data.x-65,
top: snapPoint.data.y+10,
child: const Icon(Icons.file_upload, size: 120, color: const Color(0xFFffa726),),
),
),
),
],
),
);
}
enum _DragState {
dragging,
notDragging,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment