initial
This commit is contained in:
50
lib/packages/html_view/html_view.dart
Normal file
50
lib/packages/html_view/html_view.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:hiddingsel_app/services/environment.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class HtmlView extends StatefulWidget {
|
||||
final String _html;
|
||||
final String html;
|
||||
|
||||
HtmlView(this.html)
|
||||
: _html = Uri.dataFromString(html, mimeType: 'text/html', encoding: utf8)
|
||||
.toString();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _HtmlViewState();
|
||||
}
|
||||
|
||||
class _HtmlViewState extends State<HtmlView> {
|
||||
|
||||
double? _height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
WebViewController wbController = WebViewController()
|
||||
..enableZoom(true)
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted);
|
||||
wbController.setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (String) async {
|
||||
if (_height == null) {
|
||||
var heightString = (await wbController
|
||||
.runJavaScriptReturningResult(
|
||||
'document.documentElement.scrollHeight;'));
|
||||
setState(() {
|
||||
_height = double.parse(heightString.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
wbController.loadHtmlString(widget.html);
|
||||
return SizedBox(
|
||||
height: _height ?? 1,
|
||||
child: WebViewWidget(
|
||||
controller: wbController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
lib/packages/icon_switch/icon_switch.dart
Normal file
60
lib/packages/icon_switch/icon_switch.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hiddingsel_app/constants/constant.dart';
|
||||
|
||||
/**
|
||||
* Inspiration: ToggleButtons https://www.youtube.com/watch?v=kVEguaQWGAY
|
||||
*
|
||||
*/
|
||||
|
||||
class IconSwitch extends StatelessWidget {
|
||||
final Future<bool> _initFavorite;
|
||||
final Function(bool favorite) _onFavorite;
|
||||
final Gradient _gradientColor;
|
||||
final Color _defaultColor;
|
||||
final IconData _iconFavorite;
|
||||
final IconData _iconDefavorite;
|
||||
|
||||
IconSwitch(this._onFavorite, {initFavorite, gradientColor, defaultColor = UIColors.grey5, iconFavorite = Icons.star, iconDefavorite = Icons.star_border_outlined})
|
||||
: _initFavorite = initFavorite ?? Future.value(false),
|
||||
_gradientColor = gradientColor ??
|
||||
LinearGradient(colors: [
|
||||
UIColors.white,
|
||||
UIColors.white,
|
||||
]),
|
||||
_defaultColor = defaultColor,
|
||||
_iconFavorite = iconFavorite,
|
||||
_iconDefavorite = iconDefavorite;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => FutureBuilder(
|
||||
future: _initFavorite,
|
||||
builder: (context, AsyncSnapshot snapshot) {
|
||||
bool _favorite = snapshot.data ?? false;
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) => IconButton(
|
||||
icon: _favorite
|
||||
? ShaderMask(
|
||||
shaderCallback: (Rect bounds) {
|
||||
return _gradientColor.createShader(bounds);
|
||||
},
|
||||
child: Icon(
|
||||
_iconFavorite,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
_iconDefavorite,
|
||||
color: _defaultColor,
|
||||
),
|
||||
tooltip: 'Favorite',
|
||||
onPressed: () {
|
||||
_onFavorite(!_favorite);
|
||||
setState(() {
|
||||
_favorite = !_favorite;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
109
lib/packages/image/zoomable_image.dart
Normal file
109
lib/packages/image/zoomable_image.dart
Normal file
@@ -0,0 +1,109 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ZoomableImage extends StatelessWidget {
|
||||
|
||||
final ImageProvider _image;
|
||||
final UniqueKey _key = UniqueKey();
|
||||
final ImageFrameBuilder? frameBuilder;
|
||||
final ImageLoadingBuilder? loadingBuilder;
|
||||
final ImageErrorWidgetBuilder? errorBuilder;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final Color? color;
|
||||
final Animation<double>? opacity;
|
||||
final FilterQuality filterQuality;
|
||||
final BlendMode? colorBlendMode;
|
||||
final BoxFit? fit;
|
||||
final AlignmentGeometry alignment;
|
||||
final ImageRepeat repeat;
|
||||
final Rect? centerSlice;
|
||||
final bool matchTextDirection;
|
||||
final bool gaplessPlayback;
|
||||
final String? semanticLabel;
|
||||
final bool excludeFromSemantics;
|
||||
final bool isAntiAlias;
|
||||
|
||||
ZoomableImage(this._image, {
|
||||
Key? key,
|
||||
this.frameBuilder,
|
||||
this.loadingBuilder,
|
||||
this.errorBuilder,
|
||||
this.semanticLabel,
|
||||
this.excludeFromSemantics = false,
|
||||
this.width,
|
||||
this.height,
|
||||
this.color,
|
||||
this.opacity,
|
||||
this.colorBlendMode,
|
||||
this.fit,
|
||||
this.alignment = Alignment.center,
|
||||
this.repeat = ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.matchTextDirection = false,
|
||||
this.gaplessPlayback = false,
|
||||
this.isAntiAlias = false,
|
||||
this.filterQuality = FilterQuality.low,}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
child: Hero(
|
||||
tag: 'zoomImageHero${_key.hashCode}',
|
||||
child: Image(
|
||||
image: _image,
|
||||
key: _key,
|
||||
frameBuilder: frameBuilder,
|
||||
loadingBuilder: loadingBuilder,
|
||||
errorBuilder: errorBuilder,
|
||||
semanticLabel: semanticLabel,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color,
|
||||
opacity: opacity,
|
||||
colorBlendMode: colorBlendMode,
|
||||
fit: fit,
|
||||
alignment: alignment,
|
||||
repeat: repeat,
|
||||
centerSlice: centerSlice,
|
||||
matchTextDirection: matchTextDirection,
|
||||
gaplessPlayback: gaplessPlayback,
|
||||
filterQuality: filterQuality,
|
||||
isAntiAlias: isAntiAlias,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (_) {
|
||||
return _ImageDetail(_image, _key);
|
||||
}));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _ImageDetail extends StatelessWidget {
|
||||
|
||||
final ImageProvider _image;
|
||||
|
||||
const _ImageDetail(this._image, Key key) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InteractiveViewer(
|
||||
panEnabled: true,
|
||||
minScale: 1,
|
||||
maxScale: 3,
|
||||
child: Scaffold(
|
||||
body: GestureDetector(
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: 'zoomImageHero${super.key.hashCode}',
|
||||
child: Image(image: _image),
|
||||
)
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
31
lib/packages/list_view_extension/colum_extension.dart
Normal file
31
lib/packages/list_view_extension/colum_extension.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
import 'helper.dart';
|
||||
|
||||
class ColumnExtension extends StatelessWidget {
|
||||
final Function(BuildContext, int) _itemBuilder;
|
||||
final int _itemCount;
|
||||
final MainAxisAlignment _mainAxisAlignment;
|
||||
|
||||
ColumnExtension.builder(
|
||||
{required itemBuilder, required itemCount, mainAxisAlignment})
|
||||
: _itemBuilder = itemBuilder,
|
||||
_itemCount = itemCount,
|
||||
_mainAxisAlignment = mainAxisAlignment ?? MainAxisAlignment.start;
|
||||
|
||||
ColumnExtension.separated(
|
||||
{required itemBuilder,
|
||||
required separatorBuilder,
|
||||
required int itemCount,
|
||||
mainAxisAlignment})
|
||||
: _itemBuilder = getSeparatedItemBuilder(itemBuilder, separatorBuilder),
|
||||
_itemCount = max(0, itemCount * 2 - 1),
|
||||
_mainAxisAlignment = mainAxisAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
mainAxisAlignment: _mainAxisAlignment,
|
||||
children:
|
||||
List<Widget>.generate(_itemCount, (i) => _itemBuilder(context, i)),
|
||||
);
|
||||
}
|
||||
12
lib/packages/list_view_extension/helper.dart
Normal file
12
lib/packages/list_view_extension/helper.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
IndexedWidgetBuilder getSeparatedItemBuilder(IndexedWidgetBuilder itemBuilder,
|
||||
IndexedWidgetBuilder separatorBuilder) {
|
||||
return (context, index) {
|
||||
if (index.isEven) {
|
||||
return itemBuilder(context, index ~/ 2);
|
||||
} else {
|
||||
return separatorBuilder(context, index ~/ 2);
|
||||
}
|
||||
};
|
||||
}
|
||||
31
lib/packages/list_view_extension/row_extension.dart
Normal file
31
lib/packages/list_view_extension/row_extension.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
import 'helper.dart';
|
||||
|
||||
class RowExtension extends StatelessWidget {
|
||||
final Function(BuildContext, int) _itemBuilder;
|
||||
final int _itemCount;
|
||||
final MainAxisAlignment _mainAxisAlignment;
|
||||
|
||||
RowExtension.builder(
|
||||
{required itemBuilder, required itemCount, mainAxisAlignment})
|
||||
: _itemBuilder = itemBuilder,
|
||||
_itemCount = itemCount,
|
||||
_mainAxisAlignment = mainAxisAlignment ?? MainAxisAlignment.start;
|
||||
|
||||
RowExtension.separated(
|
||||
{required itemBuilder,
|
||||
required separatorBuilder,
|
||||
required int itemCount,
|
||||
mainAxisAlignment})
|
||||
: _itemBuilder = getSeparatedItemBuilder(itemBuilder, separatorBuilder),
|
||||
_itemCount = max(0, itemCount * 2 - 1),
|
||||
_mainAxisAlignment = mainAxisAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
mainAxisAlignment: _mainAxisAlignment,
|
||||
children:
|
||||
List<Widget>.generate(_itemCount, (i) => _itemBuilder(context, i)),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user