moved non-premium features from premium folder

This commit is contained in:
Kima
2023-10-21 21:11:39 +02:00
parent 774f63aa89
commit 316039463f
25 changed files with 90 additions and 146 deletions

View File

@@ -1,137 +0,0 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// Blocky Color Picker
library block_colorpicker;
import 'package:flutter/material.dart';
import 'package:filcnaplo/theme/colors/accent.dart';
import 'utils.dart';
/// Child widget for layout builder.
typedef PickerItem = Widget Function(Color color);
/// Customize the layout.
typedef PickerLayoutBuilder = Widget Function(BuildContext context, List<Color> colors, PickerItem child);
/// Customize the item shape.
typedef PickerItemBuilder = Widget Function(Color color, bool isCurrentColor, void Function() changeColor);
// Provide a list of colors for block color picker.
// const List<Color> _defaultColors = [
// Colors.red,
// Colors.pink,
// Colors.purple,
// Colors.deepPurple,
// Colors.indigo,
// Colors.blue,
// Colors.lightBlue,
// Colors.cyan,
// Colors.teal,
// Colors.green,
// Colors.lightGreen,
// Colors.lime,
// Colors.yellow,
// Colors.amber,
// Colors.orange,
// Colors.deepOrange,
// Colors.brown,
// Colors.grey,
// Colors.blueGrey,
// Colors.black,
// ];
// Provide a layout for [BlockPicker].
Widget _defaultLayoutBuilder(BuildContext context, List<Color> colors, PickerItem child) {
Orientation orientation = MediaQuery.of(context).orientation;
return SizedBox(
width: 300,
height: orientation == Orientation.portrait ? 360 : 200,
child: GridView.count(
crossAxisCount: orientation == Orientation.portrait ? 4 : 6,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
children: [for (Color color in colors) child(color)],
),
);
}
// Provide a shape for [BlockPicker].
Widget _defaultItemBuilder(Color color, bool isCurrentColor, void Function() changeColor) {
return Container(
margin: const EdgeInsets.all(7),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
boxShadow: [BoxShadow(color: color.withOpacity(0.8), offset: const Offset(1, 2), blurRadius: 5)],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: changeColor,
borderRadius: BorderRadius.circular(50),
child: AnimatedOpacity(
duration: const Duration(milliseconds: 210),
opacity: isCurrentColor ? 1 : 0,
child: Icon(Icons.done, color: useWhiteForeground(color) ? Colors.white : Colors.black),
),
),
),
);
}
// The blocky color picker you can alter the layout and shape.
class BlockPicker extends StatefulWidget {
BlockPicker({
Key? key,
required this.pickerColor,
required this.onColorChanged,
this.useInShowDialog = true,
this.layoutBuilder = _defaultLayoutBuilder,
this.itemBuilder = _defaultItemBuilder,
}) : super(key: key);
final Color? pickerColor;
final ValueChanged<Color> onColorChanged;
final List<Color> availableColors = accentColorMap.values.toList();
final bool useInShowDialog;
final PickerLayoutBuilder layoutBuilder;
final PickerItemBuilder itemBuilder;
@override
State<StatefulWidget> createState() => _BlockPickerState();
}
class _BlockPickerState extends State<BlockPicker> {
Color? _currentColor;
@override
void initState() {
_currentColor = widget.pickerColor;
super.initState();
}
void changeColor(Color color) {
setState(() => _currentColor = color);
widget.onColorChanged(color);
}
@override
Widget build(BuildContext context) {
return widget.layoutBuilder(
context,
widget.availableColors,
(Color color) => widget.itemBuilder(
color,
(_currentColor != null && (widget.useInShowDialog ? true : widget.pickerColor != null))
? (_currentColor?.value == color.value) && (widget.useInShowDialog ? true : widget.pickerColor?.value == color.value)
: false,
() => changeColor(color),
),
);
}
}

View File

@@ -1,468 +0,0 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// HSV(HSB)/HSL Color Picker example
///
/// You can create your own layout by importing `picker.dart`.
library hsv_picker;
import 'package:filcnaplo/models/shared_theme.dart';
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
import 'package:filcnaplo_premium/providers/share_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/block_picker.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/palette.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/utils.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/theme.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/theme.i18n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:provider/provider.dart';
class FilcColorPicker extends StatefulWidget {
const FilcColorPicker({
Key? key,
required this.colorMode,
required this.pickerColor,
required this.onColorChanged,
required this.onColorChangeEnd,
this.pickerHsvColor,
this.onHsvColorChanged,
this.paletteType = PaletteType.hsvWithHue,
this.enableAlpha = true,
@Deprecated('Use empty list in [labelTypes] to disable label.')
this.showLabel = true,
this.labelTypes = const [
ColorLabelType.rgb,
ColorLabelType.hsv,
ColorLabelType.hsl
],
@Deprecated(
'Use Theme.of(context).textTheme.bodyText1 & 2 to alter text style.')
this.labelTextStyle,
this.displayThumbColor = false,
this.portraitOnly = false,
this.colorPickerWidth = 300.0,
this.pickerAreaHeightPercent = 1.0,
this.pickerAreaBorderRadius = const BorderRadius.all(Radius.zero),
this.hexInputBar = false,
this.hexInputController,
this.colorHistory,
this.onHistoryChanged,
required this.onThemeIdProvided,
}) : super(key: key);
final CustomColorMode colorMode;
final Color pickerColor;
final ValueChanged<Color> onColorChanged;
final void Function(Color color, {bool? adaptive}) onColorChangeEnd;
final HSVColor? pickerHsvColor;
final ValueChanged<HSVColor>? onHsvColorChanged;
final PaletteType paletteType;
final bool enableAlpha;
final bool showLabel;
final List<ColorLabelType> labelTypes;
final TextStyle? labelTextStyle;
final bool displayThumbColor;
final bool portraitOnly;
final double colorPickerWidth;
final double pickerAreaHeightPercent;
final BorderRadius pickerAreaBorderRadius;
final bool hexInputBar;
final TextEditingController? hexInputController;
final List<Color>? colorHistory;
final ValueChanged<List<Color>>? onHistoryChanged;
final void Function(SharedTheme theme) onThemeIdProvided;
@override
_FilcColorPickerState createState() => _FilcColorPickerState();
}
class _FilcColorPickerState extends State<FilcColorPicker> {
final idController = TextEditingController();
late final ShareProvider shareProvider;
HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
List<Color> colorHistory = [];
bool isAdvancedView = false;
@override
void initState() {
currentHsvColor = (widget.pickerHsvColor != null)
? widget.pickerHsvColor as HSVColor
: HSVColor.fromColor(widget.pickerColor);
// If there's no initial text in `hexInputController`,
if (widget.hexInputController?.text.isEmpty == true) {
// set it to the current's color HEX value.
widget.hexInputController?.text = colorToHex(
currentHsvColor.toColor(),
enableAlpha: widget.enableAlpha,
);
}
// Listen to the text input, If there is an `hexInputController` provided.
widget.hexInputController?.addListener(colorPickerTextInputListener);
if (widget.colorHistory != null && widget.onHistoryChanged != null) {
colorHistory = widget.colorHistory ?? [];
}
shareProvider = Provider.of<ShareProvider>(context, listen: false);
super.initState();
}
@override
void didUpdateWidget(FilcColorPicker oldWidget) {
super.didUpdateWidget(oldWidget);
currentHsvColor = (widget.pickerHsvColor != null)
? widget.pickerHsvColor as HSVColor
: HSVColor.fromColor(widget.pickerColor);
}
void colorPickerTextInputListener() {
// It can't be null really, since it's only listening if the controller
// is provided, but it may help to calm the Dart analyzer in the future.
if (widget.hexInputController == null) return;
// If a user is inserting/typing any text — try to get the color value from it,
// and interpret its transparency, dependent on the widget's settings.
final Color? color = colorFromHex(widget.hexInputController!.text,
enableAlpha: widget.enableAlpha);
// If it's the valid color:
if (color != null) {
// set it as the current color and
setState(() => currentHsvColor = HSVColor.fromColor(color));
// notify with a callback.
widget.onColorChanged(color);
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
}
}
@override
void dispose() {
widget.hexInputController?.removeListener(colorPickerTextInputListener);
super.dispose();
}
Widget colorPickerSlider(TrackType trackType) {
return ColorPickerSlider(
trackType,
currentHsvColor,
(HSVColor color) {
// Update text in `hexInputController` if provided.
widget.hexInputController?.text =
colorToHex(color.toColor(), enableAlpha: widget.enableAlpha);
setState(() => currentHsvColor = color);
widget.onColorChanged(currentHsvColor.toColor());
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
},
() => widget.onColorChangeEnd(currentHsvColor.toColor()),
(p) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Move the ${p == 0 ? 'Saturation (second)' : 'Value (third)'} slider first.",
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.w600)),
backgroundColor: AppColors.of(context).background));
},
displayThumbColor: widget.displayThumbColor,
);
}
void onColorChanging(HSVColor color) {
// Update text in `hexInputController` if provided.
widget.hexInputController?.text =
colorToHex(color.toColor(), enableAlpha: widget.enableAlpha);
setState(() => currentHsvColor = color);
widget.onColorChanged(currentHsvColor.toColor());
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
}
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).orientation == Orientation.portrait ||
widget.portraitOnly) {
return Column(
children: [
if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.hue),
),
),
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.saturation),
),
),
if (isAdvancedView)
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.value),
),
),
],
),
),
if (isAdvancedView &&
widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: ColorPickerInput(
currentHsvColor.toColor(),
(Color color) {
setState(() => currentHsvColor = HSVColor.fromColor(color));
widget.onColorChanged(currentHsvColor.toColor());
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
},
enableAlpha: false,
embeddedText: false,
),
),
if (widget.colorMode == CustomColorMode.enterId)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
child: Column(
children: [
TextField(
autocorrect: false,
autofocus: true,
onEditingComplete: () async {
SharedTheme? theme = await shareProvider.getThemeById(
context,
id: idController.text.replaceAll(' ', ''),
);
if (theme != null) {
widget.onThemeIdProvided(theme);
idController.clear();
} else {
ScaffoldMessenger.of(context).showSnackBar(
CustomSnackBar(
content: Text("theme_not_found".i18n,
style: const TextStyle(color: Colors.white)),
backgroundColor: AppColors.of(context).red,
context: context,
),
);
}
},
controller: idController,
decoration: InputDecoration(
hintText: 'theme_id'.i18n,
),
),
// MaterialActionButton(
// child: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text('check_id'.i18n),
// ],
// ),
// backgroundColor: AppColors.of(context).filc,
// onPressed: () {},
// ),
],
),
),
if (widget.colorMode != CustomColorMode.enterId)
SizedBox(
height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1),
child: BlockPicker(
pickerColor: Colors.red,
layoutBuilder: (context, colors, child) {
return GridView.count(
shrinkWrap: true,
crossAxisCount:
widget.colorMode == CustomColorMode.theme ? 2 : 1,
scrollDirection: Axis.horizontal,
crossAxisSpacing: 15,
physics: const BouncingScrollPhysics(),
mainAxisSpacing: 15,
padding: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
children: List.generate(
colors.toSet().length +
(widget.colorMode == CustomColorMode.theme ? 1 : 0),
(index) {
if (widget.colorMode == CustomColorMode.theme) {
if (index == 0) {
return GestureDetector(
onTap: () => widget.onColorChangeEnd(
Colors.transparent,
adaptive: true),
child: ColorIndicator(
HSVColor.fromColor(
const Color.fromARGB(255, 255, 238, 177)),
icon: CupertinoIcons.wand_stars,
currentHsvColor: currentHsvColor,
width: 30,
height: 30,
adaptive: true),
);
}
index--;
}
return GestureDetector(
onTap: () => widget.onColorChangeEnd(colors[index]),
child: ColorIndicator(HSVColor.fromColor(colors[index]),
currentHsvColor: currentHsvColor,
width: 30,
height: 30),
);
}),
);
},
onColorChanged: (c) => {},
),
),
if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Material(
color: Colors.transparent,
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
onTap: () => setState(() {
isAdvancedView = !isAdvancedView;
}),
child: Padding(
padding:
const EdgeInsets.only(left: 8.0, right: 8.0, top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Switch(
onChanged: (v) => setState(() => isAdvancedView = v),
value: isAdvancedView,
),
const SizedBox(width: 12.0),
Text(
"advanced".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context)
.text
.withOpacity(isAdvancedView ? 1.0 : .5),
),
),
],
),
),
),
),
],
);
} else {
return Row(
children: [
//SizedBox(width: widget.colorPickerWidth, height: widget.colorPickerWidth * widget.pickerAreaHeightPercent, child: colorPicker()),
Column(
children: [
Row(
children: <Widget>[
const SizedBox(width: 20.0),
GestureDetector(
onTap: () => setState(() {
if (widget.onHistoryChanged != null &&
!colorHistory.contains(currentHsvColor.toColor())) {
colorHistory.add(currentHsvColor.toColor());
widget.onHistoryChanged!(colorHistory);
}
}),
child: ColorIndicator(currentHsvColor),
),
Column(
children: <Widget>[
//SizedBox(height: 40.0, width: 260.0, child: sliderByPaletteType()),
if (widget.enableAlpha)
SizedBox(
height: 40.0,
width: 260.0,
child: colorPickerSlider(TrackType.alpha)),
],
),
const SizedBox(width: 10.0),
],
),
if (colorHistory.isNotEmpty)
SizedBox(
width: widget.colorPickerWidth,
height: 50,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
for (Color color in colorHistory)
Padding(
key: Key(color.hashCode.toString()),
padding: const EdgeInsets.fromLTRB(15, 18, 0, 0),
child: Center(
child: GestureDetector(
onTap: () =>
onColorChanging(HSVColor.fromColor(color)),
onLongPress: () {
if (colorHistory.remove(color)) {
widget.onHistoryChanged!(colorHistory);
setState(() {});
}
},
child: ColorIndicator(HSVColor.fromColor(color),
width: 30, height: 30),
),
),
),
const SizedBox(width: 15),
]),
),
const SizedBox(height: 20.0),
if (widget.hexInputBar)
ColorPickerInput(
currentHsvColor.toColor(),
(Color color) {
setState(() => currentHsvColor = HSVColor.fromColor(color));
widget.onColorChanged(currentHsvColor.toColor());
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
},
enableAlpha: widget.enableAlpha,
embeddedText: false,
),
const SizedBox(height: 5),
],
),
],
);
}
}
}

View File

@@ -1,174 +0,0 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
import 'dart:ui';
/// X11 Colors
///
/// https://en.wikipedia.org/wiki/X11_color_names
const Map<String, Color> x11Colors = {
'aliceblue': Color(0xfff0f8ff),
'antiquewhite': Color(0xfffaebd7),
'aqua': Color(0xff00ffff),
'aquamarine': Color(0xff7fffd4),
'azure': Color(0xfff0ffff),
'beige': Color(0xfff5f5dc),
'bisque': Color(0xffffe4c4),
'black': Color(0xff000000),
'blanchedalmond': Color(0xffffebcd),
'blue': Color(0xff0000ff),
'blueviolet': Color(0xff8a2be2),
'brown': Color(0xffa52a2a),
'burlywood': Color(0xffdeb887),
'cadetblue': Color(0xff5f9ea0),
'chartreuse': Color(0xff7fff00),
'chocolate': Color(0xffd2691e),
'coral': Color(0xffff7f50),
'cornflower': Color(0xff6495ed),
'cornflowerblue': Color(0xff6495ed),
'cornsilk': Color(0xfffff8dc),
'crimson': Color(0xffdc143c),
'cyan': Color(0xff00ffff),
'darkblue': Color(0xff00008b),
'darkcyan': Color(0xff008b8b),
'darkgoldenrod': Color(0xffb8860b),
'darkgray': Color(0xffa9a9a9),
'darkgreen': Color(0xff006400),
'darkgrey': Color(0xffa9a9a9),
'darkkhaki': Color(0xffbdb76b),
'darkmagenta': Color(0xff8b008b),
'darkolivegreen': Color(0xff556b2f),
'darkorange': Color(0xffff8c00),
'darkorchid': Color(0xff9932cc),
'darkred': Color(0xff8b0000),
'darksalmon': Color(0xffe9967a),
'darkseagreen': Color(0xff8fbc8f),
'darkslateblue': Color(0xff483d8b),
'darkslategray': Color(0xff2f4f4f),
'darkslategrey': Color(0xff2f4f4f),
'darkturquoise': Color(0xff00ced1),
'darkviolet': Color(0xff9400d3),
'deeppink': Color(0xffff1493),
'deepskyblue': Color(0xff00bfff),
'dimgray': Color(0xff696969),
'dimgrey': Color(0xff696969),
'dodgerblue': Color(0xff1e90ff),
'firebrick': Color(0xffb22222),
'floralwhite': Color(0xfffffaf0),
'forestgreen': Color(0xff228b22),
'fuchsia': Color(0xffff00ff),
'gainsboro': Color(0xffdcdcdc),
'ghostwhite': Color(0xfff8f8ff),
'gold': Color(0xffffd700),
'goldenrod': Color(0xffdaa520),
'gray': Color(0xff808080),
'green': Color(0xff008000),
'greenyellow': Color(0xffadff2f),
'grey': Color(0xff808080),
'honeydew': Color(0xfff0fff0),
'hotpink': Color(0xffff69b4),
'indianred': Color(0xffcd5c5c),
'indigo': Color(0xff4b0082),
'ivory': Color(0xfffffff0),
'khaki': Color(0xfff0e68c),
'laserlemon': Color(0xffffff54),
'lavender': Color(0xffe6e6fa),
'lavenderblush': Color(0xfffff0f5),
'lawngreen': Color(0xff7cfc00),
'lemonchiffon': Color(0xfffffacd),
'lightblue': Color(0xffadd8e6),
'lightcoral': Color(0xfff08080),
'lightcyan': Color(0xffe0ffff),
'lightgoldenrod': Color(0xfffafad2),
'lightgoldenrodyellow': Color(0xfffafad2),
'lightgray': Color(0xffd3d3d3),
'lightgreen': Color(0xff90ee90),
'lightgrey': Color(0xffd3d3d3),
'lightpink': Color(0xffffb6c1),
'lightsalmon': Color(0xffffa07a),
'lightseagreen': Color(0xff20b2aa),
'lightskyblue': Color(0xff87cefa),
'lightslategray': Color(0xff778899),
'lightslategrey': Color(0xff778899),
'lightsteelblue': Color(0xffb0c4de),
'lightyellow': Color(0xffffffe0),
'lime': Color(0xff00ff00),
'limegreen': Color(0xff32cd32),
'linen': Color(0xfffaf0e6),
'magenta': Color(0xffff00ff),
'maroon': Color(0xff800000),
'maroon2': Color(0xff7f0000),
'maroon3': Color(0xffb03060),
'mediumaquamarine': Color(0xff66cdaa),
'mediumblue': Color(0xff0000cd),
'mediumorchid': Color(0xffba55d3),
'mediumpurple': Color(0xff9370db),
'mediumseagreen': Color(0xff3cb371),
'mediumslateblue': Color(0xff7b68ee),
'mediumspringgreen': Color(0xff00fa9a),
'mediumturquoise': Color(0xff48d1cc),
'mediumvioletred': Color(0xffc71585),
'midnightblue': Color(0xff191970),
'mintcream': Color(0xfff5fffa),
'mistyrose': Color(0xffffe4e1),
'moccasin': Color(0xffffe4b5),
'navajowhite': Color(0xffffdead),
'navy': Color(0xff000080),
'oldlace': Color(0xfffdf5e6),
'olive': Color(0xff808000),
'olivedrab': Color(0xff6b8e23),
'orange': Color(0xffffa500),
'orangered': Color(0xffff4500),
'orchid': Color(0xffda70d6),
'palegoldenrod': Color(0xffeee8aa),
'palegreen': Color(0xff98fb98),
'paleturquoise': Color(0xffafeeee),
'palevioletred': Color(0xffdb7093),
'papayawhip': Color(0xffffefd5),
'peachpuff': Color(0xffffdab9),
'peru': Color(0xffcd853f),
'pink': Color(0xffffc0cb),
'plum': Color(0xffdda0dd),
'powderblue': Color(0xffb0e0e6),
'purple': Color(0xff800080),
'purple2': Color(0xff7f007f),
'purple3': Color(0xffa020f0),
'rebeccapurple': Color(0xff663399),
'red': Color(0xffff0000),
'rosybrown': Color(0xffbc8f8f),
'royalblue': Color(0xff4169e1),
'saddlebrown': Color(0xff8b4513),
'salmon': Color(0xfffa8072),
'sandybrown': Color(0xfff4a460),
'seagreen': Color(0xff2e8b57),
'seashell': Color(0xfffff5ee),
'sienna': Color(0xffa0522d),
'silver': Color(0xffc0c0c0),
'skyblue': Color(0xff87ceeb),
'slateblue': Color(0xff6a5acd),
'slategray': Color(0xff708090),
'slategrey': Color(0xff708090),
'snow': Color(0xfffffafa),
'springgreen': Color(0xff00ff7f),
'steelblue': Color(0xff4682b4),
'tan': Color(0xffd2b48c),
'teal': Color(0xff008080),
'thistle': Color(0xffd8bfd8),
'tomato': Color(0xffff6347),
'turquoise': Color(0xff40e0d0),
'violet': Color(0xffee82ee),
'wheat': Color(0xfff5deb3),
'white': Color(0xffffffff),
'whitesmoke': Color(0xfff5f5f5),
'yellow': Color(0xffffff00),
'yellowgreen': Color(0xff9acd32),
};
Color? colorFromName(String val) => x11Colors[val.trim().replaceAll(' ', '').toLowerCase()];
extension ColorExtension on String {
Color? toColor() => colorFromName(this);
}

View File

@@ -1,785 +0,0 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// The components of HSV Color Picker
///
/// Try to create a Color Picker with other layout on your own :)
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/accent.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'utils.dart';
/// Palette types for color picker area widget.
enum PaletteType {
hsv,
hsvWithHue,
hsvWithValue,
hsvWithSaturation,
hsl,
hslWithHue,
hslWithLightness,
hslWithSaturation,
rgbWithBlue,
rgbWithGreen,
rgbWithRed,
hueWheel,
}
/// Track types for slider picker.
enum TrackType {
hue,
saturation,
saturationForHSL,
value,
lightness,
red,
green,
blue,
alpha,
}
enum FilcTrackType {
hue,
saturation,
value,
}
/// Color information label type.
enum ColorLabelType { hex, rgb, hsv, hsl }
/// Types for slider picker widget.
enum ColorModel { rgb, hsv, hsl }
// enum ColorSpace { rgb, hsv, hsl, hsp, okhsv, okhsl, xyz, yuv, lab, lch, cmyk }
/// Painter for SV mixture.
class HSVWithHueColorPainter extends CustomPainter {
const HSVWithHueColorPainter(this.hsvColor, {this.pointerColor});
final HSVColor hsvColor;
final Color? pointerColor;
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const Gradient gradientV = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.white, Colors.black],
);
final Gradient gradientH = LinearGradient(
colors: [
Colors.white,
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
],
);
canvas.drawRect(rect, Paint()..shader = gradientV.createShader(rect));
canvas.drawRect(
rect,
Paint()
..blendMode = BlendMode.multiply
..shader = gradientH.createShader(rect),
);
canvas.drawCircle(
Offset(size.width * hsvColor.saturation, size.height * (1 - hsvColor.value)),
size.height * 0.04,
Paint()
..color = pointerColor ?? (useWhiteForeground(hsvColor.toColor()) ? Colors.white : Colors.black)
..strokeWidth = 1.5
..style = PaintingStyle.stroke,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class _SliderLayout extends MultiChildLayoutDelegate {
static const String track = 'track';
static const String thumb = 'thumb';
static const String gestureContainer = 'gesturecontainer';
@override
void performLayout(Size size) {
layoutChild(
track,
BoxConstraints.tightFor(
width: size.width + 3,
height: size.height / 1.5,
),
);
positionChild(track, const Offset(-2.0, 0));
layoutChild(
thumb,
const BoxConstraints.tightFor(width: 5.5, height: 10.5),
);
positionChild(thumb, Offset(0.0, (size.height / 1.5) / 2 - 4.5));
layoutChild(
gestureContainer,
BoxConstraints.tightFor(width: size.width, height: size.height),
);
positionChild(gestureContainer, Offset.zero);
}
@override
bool shouldRelayout(_SliderLayout oldDelegate) => false;
}
/// Painter for all kinds of track types.
class TrackPainter extends CustomPainter {
const TrackPainter(this.trackType, this.hsvColor);
final TrackType trackType;
final HSVColor hsvColor;
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
if (trackType == TrackType.alpha) {
final Size chessSize = Size(size.height / 2, size.height / 2);
Paint chessPaintB = Paint()..color = const Color(0xffcccccc);
Paint chessPaintW = Paint()..color = Colors.white;
List.generate((size.height / chessSize.height).round(), (int y) {
List.generate((size.width / chessSize.width).round(), (int x) {
canvas.drawRect(
Offset(chessSize.width * x, chessSize.width * y) & chessSize,
(x + y) % 2 != 0 ? chessPaintW : chessPaintB,
);
});
});
}
switch (trackType) {
case TrackType.hue:
final List<Color> colors = [
const HSVColor.fromAHSV(1.0, 0.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 60.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 120.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 180.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 240.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 300.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 360.0, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.saturation:
final List<Color> colors = [
HSVColor.fromAHSV(1.0, hsvColor.hue, 0.0, 1.0).toColor(),
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.saturationForHSL:
final List<Color> colors = [
HSLColor.fromAHSL(1.0, hsvColor.hue, 0.0, 0.5).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.5).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.value:
final List<Color> colors = [
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 0.0).toColor(),
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.lightness:
final List<Color> colors = [
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.0).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.5).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.red:
final List<Color> colors = [
hsvColor.toColor().withRed(0).withOpacity(1.0),
hsvColor.toColor().withRed(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.green:
final List<Color> colors = [
hsvColor.toColor().withGreen(0).withOpacity(1.0),
hsvColor.toColor().withGreen(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.blue:
final List<Color> colors = [
hsvColor.toColor().withBlue(0).withOpacity(1.0),
hsvColor.toColor().withBlue(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.alpha:
final List<Color> colors = [
hsvColor.toColor().withOpacity(0.0),
hsvColor.toColor().withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Painter for thumb of slider.
class ThumbPainter extends CustomPainter {
const ThumbPainter({this.thumbColor, this.fullThumbColor = false});
final Color? thumbColor;
final bool fullThumbColor;
@override
void paint(Canvas canvas, Size size) {
canvas.drawShadow(
Path()
..addOval(
Rect.fromCircle(center: const Offset(0.5, 2.0), radius: size.width * 1.8),
),
Colors.black,
3.0,
true,
);
canvas.drawCircle(
Offset(0.0, size.height * 0.4),
size.height,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
if (thumbColor != null) {
canvas.drawCircle(
Offset(0.0, size.height * 0.4),
size.height * (fullThumbColor ? 1.0 : 0.65),
Paint()
..color = thumbColor!
..style = PaintingStyle.fill);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Painter for chess type alpha background in color indicator widget.
class IndicatorPainter extends CustomPainter {
const IndicatorPainter(this.color);
final Color color;
@override
void paint(Canvas canvas, Size size) {
final Size chessSize = Size(size.width / 10, size.height / 10);
final Paint chessPaintB = Paint()..color = const Color(0xFFCCCCCC);
final Paint chessPaintW = Paint()..color = Colors.white;
List.generate((size.height / chessSize.height).round(), (int y) {
List.generate((size.width / chessSize.width).round(), (int x) {
canvas.drawRect(
Offset(chessSize.width * x, chessSize.height * y) & chessSize,
(x + y) % 2 != 0 ? chessPaintW : chessPaintB,
);
});
});
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.height / 2,
Paint()
..color = color
..style = PaintingStyle.fill);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Provide hex input wiget for 3/6/8 digits.
class ColorPickerInput extends StatefulWidget {
const ColorPickerInput(
this.color,
this.onColorChanged, {
Key? key,
this.enableAlpha = true,
this.embeddedText = false,
this.disable = false,
}) : super(key: key);
final Color color;
final ValueChanged<Color> onColorChanged;
final bool enableAlpha;
final bool embeddedText;
final bool disable;
@override
_ColorPickerInputState createState() => _ColorPickerInputState();
}
class _ColorPickerInputState extends State<ColorPickerInput> {
TextEditingController textEditingController = TextEditingController();
int inputColor = 0;
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (inputColor != widget.color.value) {
textEditingController.text = '#' +
widget.color.red.toRadixString(16).toUpperCase().padLeft(2, '0') +
widget.color.green.toRadixString(16).toUpperCase().padLeft(2, '0') +
widget.color.blue.toRadixString(16).toUpperCase().padLeft(2, '0') +
(widget.enableAlpha ? widget.color.alpha.toRadixString(16).toUpperCase().padLeft(2, '0') : '');
}
return Padding(
padding: const EdgeInsets.only(top: 6.0, left: 12.0, right: 12.0),
child: SizedBox(
width: double.infinity,
child: TextField(
enabled: !widget.disable,
controller: textEditingController,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).colorScheme.onBackground,
),
inputFormatters: [
UpperCaseTextFormatter(),
FilteringTextInputFormatter.allow(RegExp(kValidHexPattern)),
],
decoration: InputDecoration(
isDense: true,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
fillColor: AppColors.of(context).text.withOpacity(.1),
),
onChanged: (String value) {
String input = value;
if (value.length == 9) {
input = value.split('').getRange(7, 9).join() + value.split('').getRange(1, 7).join();
}
final Color? color = colorFromHex(input);
if (color != null) {
widget.onColorChanged(color);
inputColor = color.value;
}
},
),
),
);
}
}
/*class ValueColorPickerSlider extends StatefulWidget {
ValueColorPickerSlider(this.trackType, this.initialHsvColor, this.onProgressChanged, this.onColorChangeEnd, {Key? key}) : super(key: key);
final TrackType trackType;
final HSVColor initialHsvColor;
final void Function(double progress) onProgressChanged;
final void Function() onColorChangeEnd;
@override
State<ValueColorPickerSlider> createState() => _ValueColorPickerSliderState();
}
class _ValueColorPickerSliderState extends State<ValueColorPickerSlider> {
HSVColor hsvColor = HSVColor.fromColor(Colors.red);
@override
void initState() {
super.initState();
hsvColor = widget.initialHsvColor;
}
void slideEvent(RenderBox getBox, BoxConstraints box, Offset globalPosition) {
double localDx = getBox.globalToLocal(globalPosition).dx - 15.0;
double progress = localDx.clamp(0.0, box.maxWidth - 30.0) / (box.maxWidth - 30.0);
setState(() {
switch (widget.trackType) {
case TrackType.hue:
hsvColor = hsvColor.withHue(progress * 359);
break;
case TrackType.saturation:
hsvColor = hsvColor.withSaturation(progress);
break;
case TrackType.value:
hsvColor = hsvColor.withValue(progress);
break;
default:
break;
}
});
widget.onProgressChanged(progress);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) {
double thumbOffset = 15.0;
Color thumbColor = Colors.white;
switch (widget.trackType) {
case TrackType.hue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.hue / 360;
break;
case TrackType.saturation:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.saturation;
break;
case TrackType.value:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.value;
break;
default:
break;
}
return CustomMultiChildLayout(
delegate: _SliderLayout(),
children: <Widget>[
LayoutId(
id: _SliderLayout.track,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50.0)),
child: CustomPaint(
painter: TrackPainter(
TrackType.values.firstWhere((element) => element == widget.trackType),
hsvColor,
)),
),
),
LayoutId(
id: _SliderLayout.thumb,
child: Transform.translate(
offset: Offset(thumbOffset, 0.0),
child: CustomPaint(
painter: ThumbPainter(
thumbColor: thumbColor,
fullThumbColor: false,
),
),
),
),
LayoutId(
id: _SliderLayout.gestureContainer,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints box) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
return GestureDetector(
onPanDown: (DragDownDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
onPanEnd: (details) => widget.onColorChangeEnd(),
onPanUpdate: (DragUpdateDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
);
},
),
),
],
);
});
}
}*/
/// 9 track types for slider picker widget.
class ColorPickerSlider extends StatelessWidget {
const ColorPickerSlider(
this.trackType,
this.hsvColor,
this.onColorChanged,
this.onColorChangeEnd,
this.onProblem, {
Key? key,
this.displayThumbColor = false,
this.fullThumbColor = false,
}) : super(key: key);
final TrackType trackType;
final HSVColor hsvColor;
final ValueChanged<HSVColor> onColorChanged;
final void Function() onColorChangeEnd;
final void Function(int v) onProblem;
final bool displayThumbColor;
final bool fullThumbColor;
void slideEvent(RenderBox getBox, BoxConstraints box, Offset globalPosition) {
double localDx = getBox.globalToLocal(globalPosition).dx - 15.0;
double progress = localDx.clamp(0.0, box.maxWidth - 30.0) / (box.maxWidth - 30.0);
switch (trackType) {
case TrackType.hue:
// 360 is the same as zero
// if set to 360, sliding to end goes to zero
final newColor = hsvColor.withHue(progress * 359);
if (newColor.saturation == 0) {
onProblem(0);
return;
}
onColorChanged(newColor);
break;
case TrackType.saturation:
final newColor = hsvColor.withSaturation(progress);
if (newColor.value == 0) {
onProblem(1);
return;
}
onColorChanged(newColor);
break;
case TrackType.value:
onColorChanged(hsvColor.withValue(progress));
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) {
double thumbOffset = 15.0;
Color thumbColor;
switch (trackType) {
case TrackType.hue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.hue / 360;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor();
break;
case TrackType.saturation:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.saturation;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, hsvColor.saturation, 1.0).toColor();
break;
case TrackType.saturationForHSL:
thumbOffset += (box.maxWidth - 30.0) * hsvToHsl(hsvColor).saturation;
thumbColor = HSLColor.fromAHSL(1.0, hsvColor.hue, hsvToHsl(hsvColor).saturation, 0.5).toColor();
break;
case TrackType.value:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.value;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, hsvColor.value).toColor();
break;
case TrackType.lightness:
thumbOffset += (box.maxWidth - 30.0) * hsvToHsl(hsvColor).lightness;
thumbColor = HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, hsvToHsl(hsvColor).lightness).toColor();
break;
case TrackType.red:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().red / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.green:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().green / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.blue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().blue / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.alpha:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().opacity;
thumbColor = hsvColor.toColor().withOpacity(hsvColor.alpha);
break;
}
return CustomMultiChildLayout(
delegate: _SliderLayout(),
children: <Widget>[
LayoutId(
id: _SliderLayout.track,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50.0)),
child: CustomPaint(
painter: TrackPainter(
trackType,
hsvColor,
)),
),
),
LayoutId(
id: _SliderLayout.thumb,
child: Transform.translate(
offset: Offset(thumbOffset, 0.0),
child: CustomPaint(
painter: ThumbPainter(
thumbColor: displayThumbColor ? thumbColor : null,
fullThumbColor: fullThumbColor,
),
),
),
),
LayoutId(
id: _SliderLayout.gestureContainer,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints box) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
return GestureDetector(
onPanDown: (DragDownDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
onPanEnd: (details) {
if ((trackType == TrackType.hue && hsvColor.saturation == 0) || (trackType == TrackType.saturation && hsvColor.value == 0)) {
return;
}
onColorChangeEnd();
},
onPanUpdate: (DragUpdateDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
);
},
),
),
],
);
});
}
}
/// Simple round color indicator.
class ColorIndicator extends StatelessWidget {
const ColorIndicator(
this.hsvColor, {
Key? key,
this.currentHsvColor,
this.icon,
this.width = 50.0,
this.height = 50.0,
this.adaptive = false,
}) : super(key: key);
final HSVColor hsvColor;
final HSVColor? currentHsvColor;
final double width;
final double height;
final IconData? icon;
final bool adaptive;
@override
Widget build(BuildContext context) {
Color color = hsvColor.toColor();
return Container(
width: width,
height: height,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
boxShadow: [
BoxShadow(
color: useWhiteForeground(color) ? Colors.white.withOpacity(.5) : Colors.black.withOpacity(.5),
offset: const Offset(0, 0),
blurRadius: 5)
],
),
child: Material(
color: Colors.transparent,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 210),
opacity: (icon != null || currentHsvColor == hsvColor) &&
(adaptive || Provider.of<SettingsProvider>(context, listen: false).accentColor != AccentColor.adaptive)
? 1
: 0,
child: Icon(icon ?? Icons.done, color: useWhiteForeground(color) ? Colors.white : Colors.black),
),
),
);
}
}
/// Provide Rectangle & Circle 2 categories, 10 variations of palette widget.
class ColorPickerArea extends StatelessWidget {
const ColorPickerArea(
this.hsvColor,
this.onColorChanged,
this.onChangeEnd,
this.paletteType, {
Key? key,
}) : super(key: key);
final HSVColor hsvColor;
final ValueChanged<HSVColor> onColorChanged;
final void Function() onChangeEnd;
final PaletteType paletteType;
/*void _handleColorRectChange(double horizontal, double vertical) {
onColorChanged(hsvColor.withSaturation(horizontal).withValue(vertical));
}*/
void _handleGesture(Offset position, BuildContext context, double height, double width) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
if (getBox == null) return;
Offset localOffset = getBox.globalToLocal(position);
double horizontal = localOffset.dx.clamp(0.0, width);
double vertical = localOffset.dy.clamp(0.0, height);
//_handleColorRectChange(horizontal / width, 1 - vertical / height);
onColorChanged(hsvColor.withSaturation(horizontal).withValue(vertical));
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double width = constraints.maxWidth;
double height = constraints.maxHeight;
return RawGestureDetector(
gestures: {
_AlwaysWinPanGestureRecognizer: GestureRecognizerFactoryWithHandlers<_AlwaysWinPanGestureRecognizer>(
() => _AlwaysWinPanGestureRecognizer(),
(_AlwaysWinPanGestureRecognizer instance) {
instance
..onDown = ((details) => _handleGesture(details.globalPosition, context, height, width))
..onEnd = ((d) => onChangeEnd())
..onUpdate = ((details) => _handleGesture(details.globalPosition, context, height, width));
},
),
},
child: Builder(
builder: (BuildContext _) {
return CustomPaint(painter: HSVWithHueColorPainter(hsvColor));
},
),
);
},
);
}
}
class _AlwaysWinPanGestureRecognizer extends PanGestureRecognizer {
@override
void addAllowedPointer(event) {
super.addAllowedPointer(event);
resolve(GestureDisposition.accepted);
}
@override
String get debugDescription => 'alwaysWin';
}
/// Uppercase text formater
class UpperCaseTextFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(oldValue, TextEditingValue newValue) =>
TextEditingValue(text: newValue.text.toUpperCase(), selection: newValue.selection);
}

View File

@@ -1,220 +0,0 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// Common function lib
import 'dart:math';
import 'package:flutter/painting.dart';
import 'colors.dart';
/// Check if is good condition to use white foreground color by passing
/// the background color, and optional bias.
///
/// Reference:
///
/// Old: https://www.w3.org/TR/WCAG20-TECHS/G18.html
///
/// New: https://github.com/mchome/flutter_statusbarcolor/issues/40
bool useWhiteForeground(Color backgroundColor, {double bias = 0.0}) {
// Old:
// return 1.05 / (color.computeLuminance() + 0.05) > 4.5;
// New:
int v = sqrt(pow(backgroundColor.red, 2) * 0.299 +
pow(backgroundColor.green, 2) * 0.587 +
pow(backgroundColor.blue, 2) * 0.114)
.round();
return v < 130 + bias ? true : false;
}
/// Convert HSV to HSL
///
/// Reference: https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_HSL
HSLColor hsvToHsl(HSVColor color) {
double s = 0.0;
double l = 0.0;
l = (2 - color.saturation) * color.value / 2;
if (l != 0) {
if (l == 1) {
s = 0.0;
} else if (l < 0.5) {
s = color.saturation * color.value / (l * 2);
} else {
s = color.saturation * color.value / (2 - l * 2);
}
}
return HSLColor.fromAHSL(
color.alpha,
color.hue,
s.clamp(0.0, 1.0),
l.clamp(0.0, 1.0),
);
}
/// Convert HSL to HSV
///
/// Reference: https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV
HSVColor hslToHsv(HSLColor color) {
double s = 0.0;
double v = 0.0;
v = color.lightness + color.saturation * (color.lightness < 0.5 ? color.lightness : 1 - color.lightness);
if (v != 0) s = 2 - 2 * color.lightness / v;
return HSVColor.fromAHSV(
color.alpha,
color.hue,
s.clamp(0.0, 1.0),
v.clamp(0.0, 1.0),
);
}
/// [RegExp] pattern for validation HEX color [String] inputs, allows only:
///
/// * exactly 1 to 8 digits in HEX format,
/// * only Latin A-F characters, case insensitive,
/// * and integer numbers 0,1,2,3,4,5,6,7,8,9,
/// * with optional hash (`#`) symbol at the beginning (not calculated in length).
///
/// ```dart
/// final RegExp hexInputValidator = RegExp(kValidHexPattern);
/// if (hexInputValidator.hasMatch(hex)) print('$hex might be a valid HEX color');
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
const String kValidHexPattern = r'^#?[0-9a-fA-F]{1,8}';
/// [RegExp] pattern for validation complete HEX color [String], allows only:
///
/// * exactly 6 or 8 digits in HEX format,
/// * only Latin A-F characters, case insensitive,
/// * and integer numbers 0,1,2,3,4,5,6,7,8,9,
/// * with optional hash (`#`) symbol at the beginning (not calculated in length).
///
/// ```dart
/// final RegExp hexCompleteValidator = RegExp(kCompleteValidHexPattern);
/// if (hexCompleteValidator.hasMatch(hex)) print('$hex is valid HEX color');
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
const String kCompleteValidHexPattern = r'^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$';
/// Try to convert text input or any [String] to valid [Color].
/// The [String] must be provided in one of those formats:
///
/// * RGB
/// * #RGB
/// * RRGGBB
/// * #RRGGBB
/// * AARRGGBB
/// * #AARRGGBB
///
/// Where: A stands for Alpha, R for Red, G for Green, and B for blue color.
/// It will only accept 3/6/8 long HEXs with an optional hash (`#`) at the beginning.
/// Allowed characters are Latin A-F case insensitive and numbers 0-9.
/// Optional [enableAlpha] can be provided (it's `true` by default). If it's set
/// to `false` transparency information (alpha channel) will be removed.
/// ```dart
/// /// // Valid 3 digit HEXs:
/// colorFromHex('abc') == Color(0xffaabbcc)
/// colorFromHex('ABc') == Color(0xffaabbcc)
/// colorFromHex('ABC') == Color(0xffaabbcc)
/// colorFromHex('#Abc') == Color(0xffaabbcc)
/// colorFromHex('#abc') == Color(0xffaabbcc)
/// colorFromHex('#ABC') == Color(0xffaabbcc)
/// // Valid 6 digit HEXs:
/// colorFromHex('aabbcc') == Color(0xffaabbcc)
/// colorFromHex('AABbcc') == Color(0xffaabbcc)
/// colorFromHex('AABBCC') == Color(0xffaabbcc)
/// colorFromHex('#AABbcc') == Color(0xffaabbcc)
/// colorFromHex('#aabbcc') == Color(0xffaabbcc)
/// colorFromHex('#AABBCC') == Color(0xffaabbcc)
/// // Valid 8 digit HEXs:
/// colorFromHex('ffaabbcc') == Color(0xffaabbcc)
/// colorFromHex('ffAABbcc') == Color(0xffaabbcc)
/// colorFromHex('ffAABBCC') == Color(0xffaabbcc)
/// colorFromHex('ffaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('FFAAbbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('ffAABBCC', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('FFaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#ffaabbcc') == Color(0xffaabbcc)
/// colorFromHex('#ffAABbcc') == Color(0xffaabbcc)
/// colorFromHex('#FFAABBCC') == Color(0xffaabbcc)
/// colorFromHex('#ffaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#FFAAbbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#ffAABBCC', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#FFaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// // Invalid HEXs:
/// colorFromHex('bc') == null // length 2
/// colorFromHex('aabbc') == null // length 5
/// colorFromHex('#ffaabbccd') == null // length 9 (+#)
/// colorFromHex('aabbcx') == null // x character
/// colorFromHex('#aabbвв') == null // в non-latin character
/// colorFromHex('') == null // empty
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
Color? colorFromHex(String inputString, {bool enableAlpha = true}) {
// Registers validator for exactly 6 or 8 digits long HEX (with optional #).
final RegExp hexValidator = RegExp(kCompleteValidHexPattern);
// Validating input, if it does not match — it's not proper HEX.
if (!hexValidator.hasMatch(inputString)) return null;
// Remove optional hash if exists and convert HEX to UPPER CASE.
String hexToParse = inputString.replaceFirst('#', '').toUpperCase();
// It may allow HEXs with transparency information even if alpha is disabled,
if (!enableAlpha && hexToParse.length == 8) {
// but it will replace this info with 100% non-transparent value (FF).
hexToParse = 'FF${hexToParse.substring(2)}';
}
// HEX may be provided in 3-digits format, let's just duplicate each letter.
if (hexToParse.length == 3) {
hexToParse = hexToParse.split('').expand((i) => [i * 2]).join();
}
// We will need 8 digits to parse the color, let's add missing digits.
if (hexToParse.length == 6) hexToParse = 'FF$hexToParse';
// HEX must be valid now, but as a precaution, it will just "try" to parse it.
final intColorValue = int.tryParse(hexToParse, radix: 16);
// If for some reason HEX is not valid — abort the operation, return nothing.
if (intColorValue == null) return null;
// Register output color for the last step.
final color = Color(intColorValue);
// Decide to return color with transparency information or not.
return enableAlpha ? color : color.withAlpha(255);
}
/// Converts `dart:ui` [Color] to the 6/8 digits HEX [String].
///
/// Prefixes a hash (`#`) sign if [includeHashSign] is set to `true`.
/// The result will be provided as UPPER CASE, it can be changed via [toUpperCase]
/// flag set to `false` (default is `true`). Hex can be returned without alpha
/// channel information (transparency), with the [enableAlpha] flag set to `false`.
String colorToHex(
Color color, {
bool includeHashSign = false,
bool enableAlpha = true,
bool toUpperCase = true,
}) {
final String hex = (includeHashSign ? '#' : '') +
(enableAlpha ? _padRadix(color.alpha) : '') +
_padRadix(color.red) +
_padRadix(color.green) +
_padRadix(color.blue);
return toUpperCase ? hex.toUpperCase() : hex;
}
// Shorthand for padLeft of RadixString, DRY.
String _padRadix(int value) => value.toRadixString(16).padLeft(2, '0');
// Extension for String
extension ColorExtension1 on String {
Color? toColor() {
Color? color = colorFromName(this);
if (color != null) return color;
return colorFromHex(this);
}
}
// Extension from Color
extension ColorExtension2 on Color {
String toHexString({bool includeHashSign = false, bool enableAlpha = true, bool toUpperCase = true}) =>
colorToHex(this, includeHashSign: false, enableAlpha: true, toUpperCase: true);
}

View File

@@ -1,105 +0,0 @@
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/grades_page.i18n.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
final Map<int, String> avgDropItems = {
0: "annual_average",
90: "3_months_average",
30: "30_days_average",
14: "14_days_average",
7: "7_days_average",
};
class PremiumAverageSelector extends StatefulWidget {
const PremiumAverageSelector({Key? key, this.onChanged, required this.value}) : super(key: key);
final Function(int?)? onChanged;
final int value;
@override
_PremiumAverageSelectorState createState() => _PremiumAverageSelectorState();
}
class _PremiumAverageSelectorState extends State<PremiumAverageSelector> {
@override
Widget build(BuildContext context) {
List<DropdownMenuItem<int>> dropdownItems = avgDropItems.keys.map((item) {
return DropdownMenuItem<int>(
value: item,
child: Text(
avgDropItems[item]!.i18n,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: AppColors.of(context).text,
),
overflow: TextOverflow.ellipsis,
),
);
}).toList();
return DropdownButton2<int>(
items: dropdownItems,
onChanged: (int? value) {
if (Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.gradeStats)) {
if (widget.onChanged != null) {
setState(() {
widget.onChanged!(value);
});
}
} else {
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.gradestats);
}
},
value: widget.value,
iconSize: 14,
iconEnabledColor: AppColors.of(context).text,
iconDisabledColor: AppColors.of(context).text,
underline: const SizedBox(),
itemHeight: 40,
itemPadding: const EdgeInsets.only(left: 14, right: 14),
dropdownWidth: 200,
dropdownPadding: null,
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
),
dropdownElevation: 8,
scrollbarRadius: const Radius.circular(40),
scrollbarThickness: 6,
scrollbarAlwaysShow: true,
offset: const Offset(-10, -10),
buttonSplashColor: Colors.transparent,
customButton: SizedBox(
height: 30,
child: Row(
children: [
Text(
avgDropItems[widget.value]!.i18n,
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(fontWeight: FontWeight.w600, color: AppColors.of(context).text.withOpacity(0.65)),
),
const SizedBox(
width: 4,
),
Icon(
FeatherIcons.chevronDown,
size: 16,
color: AppColors.of(context).text,
),
],
),
),
);
}
}

View File

@@ -1,39 +0,0 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_helper.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo/utils/format.dart';
class PremiumIconPackSelector extends StatelessWidget {
const PremiumIconPackSelector({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final settings = Provider.of<SettingsProvider>(context);
return PanelButton(
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.customIcons)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.iconpack);
return;
}
SettingsHelper.iconPack(context);
},
title: Text("icon_pack".i18n),
leading: const Icon(FeatherIcons.grid),
trailing: Text(
settings.iconPack.name.capital(),
style: const TextStyle(fontSize: 14.0),
),
);
}
}

View File

@@ -1,72 +0,0 @@
import 'package:i18n_extension/i18n_extension.dart';
extension SettingsLocalization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
// subject rename
"renamed_subjects": "Renamed Subjects",
"rename_subjects": "Rename Subjects",
"rename_subject": "Rename Subject",
"select_subject": "Select Subject",
"modified_name": "Modified Name",
"modify_subjects": "Modify Subjects",
"cancel": "Cancel",
"done": "Done",
"rename_new_subject": "Rename New Subject",
"italics_toggle": "Italic Font",
// teacher rename
"renamed_teachers": "Renamed Teachers",
"rename_teachers": "Rename Teachers",
"rename_teacher": "Rename Teacher",
"select_teacher": "Select Teacher",
"modify_teachers": "Modify Teachers",
"rename_new_teacher": "Rename New Teacher",
},
"hu_hu": {
// subject rename
"renamed_subjects": "Átnevezett Tantárgyaid",
"rename_subjects": "Tantárgyak átnevezése",
"rename_subject": "Tantárgy átnevezése",
"select_subject": "Válassz tantárgyat",
"modified_name": "Módosított név",
"modify_subjects": "Tantárgyak módosítása",
"cancel": "Mégse",
"done": "Kész",
"rename_new_subject": "Új tantárgy átnevezése",
"italics_toggle": "Dőlt betűs megjelenítés",
// teacher rename
"renamed_teachers": "Átnevezett tanáraid",
"rename_teachers": "Tanárok átnevezése",
"rename_teacher": "Tanár átnevezése",
"select_teacher": "Válassz tanárt",
"modify_teachers": "Tanárok módosítása",
"rename_new_teacher": "Új tanár átnevezése",
},
"de_de": {
// subject rename
"renamed_subjects": "Umbenannte Fächer",
"rename_subjects": "Fächer umbenennen",
"rename_subject": "Fach umbenennen",
"select_subject": "Fach auswählen",
"modified_name": "Geänderter Name",
"modify_subjects": "Fächer ändern",
"cancel": "Abbrechen",
"done": "Erledigt",
"rename_new_subject": "Neues Fach umbenennen",
"italics_toggle": "Kursivschrift umschalten",
// teacher rename
"renamed_teachers": "Renamed Teachers",
"rename_teachers": "Rename Teachers",
"rename_teacher": "Rename Teacher",
"select_teacher": "Select Teacher",
"modify_teachers": "Modify Teachers",
"rename_new_teacher": "Rename New Teacher",
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -1,438 +0,0 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'modify_names.i18n.dart';
class MenuRenamedSubjects extends StatelessWidget {
const MenuRenamedSubjects({Key? key, required this.settings})
: super(key: key);
final SettingsProvider settings;
@override
Widget build(BuildContext context) {
return PanelButton(
padding: const EdgeInsets.only(left: 14.0),
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.subjectrename);
return;
}
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(builder: (context) => const ModifySubjectNames()),
);
},
title: Text(
"rename_subjects".i18n,
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
),
leading: settings.renamedSubjectsEnabled
? const Icon(FeatherIcons.penTool)
: Icon(FeatherIcons.penTool,
color: AppColors.of(context).text.withOpacity(.25)),
trailingDivider: true,
trailing: Switch(
onChanged: (v) async {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.subjectrename);
return;
}
settings.update(renamedSubjectsEnabled: v);
await Provider.of<GradeProvider>(context, listen: false)
.convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false)
.convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false)
.convertBySettings();
},
value: settings.renamedSubjectsEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
),
);
}
}
class ModifySubjectNames extends StatefulWidget {
const ModifySubjectNames({Key? key}) : super(key: key);
@override
State<ModifySubjectNames> createState() => _ModifySubjectNamesState();
}
class _ModifySubjectNamesState extends State<ModifySubjectNames> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final _subjectName = TextEditingController();
String? selectedSubjectId;
late List<GradeSubject> subjects;
late UserProvider user;
late DatabaseProvider dbProvider;
late SettingsProvider settings;
@override
void initState() {
super.initState();
subjects = Provider.of<GradeProvider>(context, listen: false)
.grades
.map((e) => e.subject)
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
user = Provider.of<UserProvider>(context, listen: false);
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
}
Future<Map<String, String>> fetchRenamedSubjects() async {
return await dbProvider.userQuery.renamedSubjects(userId: user.id!);
}
void showRenameDialog() {
showDialog(
context: context,
builder: (context) => StatefulBuilder(builder: (context, setS) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
title: Text("rename_subject".i18n),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton2(
items: subjects
.map((item) => DropdownMenuItem<String>(
value: item.id,
child: Text(
item.name,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: AppColors.of(context).text,
),
overflow: TextOverflow.ellipsis,
),
))
.toList(),
onChanged: (String? v) async {
final renamedSubs = await fetchRenamedSubjects();
setS(() {
selectedSubjectId = v;
if (renamedSubs.containsKey(selectedSubjectId)) {
_subjectName.text = renamedSubs[selectedSubjectId]!;
} else {
_subjectName.text = "";
}
});
},
iconSize: 14,
iconEnabledColor: AppColors.of(context).text,
iconDisabledColor: AppColors.of(context).text,
underline: const SizedBox(),
itemHeight: 40,
itemPadding: const EdgeInsets.only(left: 14, right: 14),
buttonWidth: 50,
dropdownWidth: 300,
dropdownPadding: null,
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
),
dropdownElevation: 8,
scrollbarRadius: const Radius.circular(40),
scrollbarThickness: 6,
scrollbarAlwaysShow: true,
offset: const Offset(-10, -10),
buttonSplashColor: Colors.transparent,
customButton: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 8.0),
child: Text(
selectedSubjectId == null
? "select_subject".i18n
: subjects
.firstWhere(
(element) => element.id == selectedSubjectId)
.name,
style: Theme.of(context).textTheme.titleSmall!.copyWith(
fontWeight: FontWeight.w700,
color: AppColors.of(context).text.withOpacity(0.75)),
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.center,
),
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Icon(FeatherIcons.arrowDown, size: 32),
),
TextField(
controller: _subjectName,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
hintText: "modified_name".i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_subjectName.text = "";
});
},
),
),
),
],
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"done".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () async {
if (selectedSubjectId != null) {
final renamedSubs = await fetchRenamedSubjects();
renamedSubs[selectedSubjectId!] = _subjectName.text;
await dbProvider.userStore
.storeRenamedSubjects(renamedSubs, userId: user.id!);
await Provider.of<GradeProvider>(context, listen: false)
.convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false)
.convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false)
.convertBySettings();
}
Navigator.of(context).pop(true);
setState(() {});
},
),
],
);
}),
).then((val) {
_subjectName.text = "";
selectedSubjectId = null;
});
}
@override
Widget build(BuildContext context) {
settings = Provider.of<SettingsProvider>(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
title: Text(
"modify_subjects".i18n,
style: TextStyle(color: AppColors.of(context).text),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Panel(
child: SwitchListTile(
title: Text("italics_toggle".i18n),
onChanged: (value) =>
settings.update(renamedSubjectsItalics: value),
value: settings.renamedSubjectsItalics,
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: showRenameDialog,
borderRadius: BorderRadius.circular(12.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.symmetric(
vertical: 18.0, horizontal: 12.0),
child: Center(
child: Text(
"rename_new_subject".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18,
color: AppColors.of(context).text.withOpacity(.85),
),
),
),
),
),
const SizedBox(
height: 30,
),
FutureBuilder<Map<String, String>>(
future: fetchRenamedSubjects(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Container();
}
return Panel(
title: Text("renamed_subjects".i18n),
child: Column(
children: snapshot.data!.keys.map(
(key) {
GradeSubject? subject = subjects
.firstWhere((element) => key == element.id);
String renameTo = snapshot.data![key]!;
return RenamedSubjectItem(
subject: subject,
renamedTo: renameTo,
modifyCallback: () {
setState(() {
selectedSubjectId = subject.id;
_subjectName.text = renameTo;
});
showRenameDialog();
},
removeCallback: () {
setState(() {
Map<String, String> subs =
Map.from(snapshot.data!);
subs.remove(key);
dbProvider.userStore.storeRenamedSubjects(
subs,
userId: user.id!);
});
},
);
},
).toList(),
),
);
},
),
],
),
),
));
}
}
class RenamedSubjectItem extends StatelessWidget {
const RenamedSubjectItem({
Key? key,
required this.subject,
required this.renamedTo,
required this.modifyCallback,
required this.removeCallback,
}) : super(key: key);
final GradeSubject subject;
final String renamedTo;
final void Function() modifyCallback;
final void Function() removeCallback;
@override
Widget build(BuildContext context) {
return ListTile(
minLeadingWidth: 32.0,
dense: true,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
visualDensity: VisualDensity.compact,
onTap: () {},
leading: Icon(
SubjectIcon.resolveVariant(subject: subject, context: context),
color: AppColors.of(context).text.withOpacity(.75)),
title: InkWell(
onTap: modifyCallback,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
subject.name.capital(),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
color: AppColors.of(context).text.withOpacity(.75)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
renamedTo,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
trailing: InkWell(
onTap: removeCallback,
child: Icon(FeatherIcons.trash,
color: AppColors.of(context).red.withOpacity(.75)),
),
);
}
}

View File

@@ -17,8 +17,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'modify_names.i18n.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/modify_names.i18n.dart';
class MenuRenamedTeachers extends StatelessWidget {
const MenuRenamedTeachers({Key? key, required this.settings})

View File

@@ -1,105 +0,0 @@
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class UserMenuNickname extends StatelessWidget {
late User u;
UserMenuNickname(this.u, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BottomSheetMenuItem(
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.nickname)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.profile);
return;
}
showDialog(
context: context, builder: (context) => UserNicknameEditor(u));
},
icon: const Icon(FeatherIcons.edit2),
title: Text("edit_nickname".i18n),
);
}
}
// ignore: must_be_immutable
class UserNicknameEditor extends StatefulWidget {
late User u;
UserNicknameEditor(this.u, {Key? key}) : super(key: key);
@override
State<UserNicknameEditor> createState() => _UserNicknameEditorState();
}
class _UserNicknameEditorState extends State<UserNicknameEditor> {
final _userName = TextEditingController();
late final UserProvider user;
@override
void initState() {
super.initState();
user = Provider.of<UserProvider>(context, listen: false);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("change_username".i18n),
content: TextField(
controller: _userName,
autofocus: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
label: Text(widget.u.name),
suffixIcon: IconButton(
icon: const Icon(FeatherIcons.x),
onPressed: () {
setState(() {
_userName.text = "";
});
},
),
),
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"done".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
widget.u.nickname = _userName.text.trim();
Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(widget.u);
Provider.of<UserProvider>(context, listen: false).refresh();
Navigator.of(context).pop(true);
},
),
],
);
}
}

View File

@@ -1,234 +0,0 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:provider/provider.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_crop/image_crop.dart';
// ignore: must_be_immutable
class UserMenuProfilePic extends StatelessWidget {
late User u;
UserMenuProfilePic(this.u, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
if (!Provider.of<PremiumProvider>(context)
.hasScope(PremiumScopes.nickname)) {
return const SizedBox();
}
return BottomSheetMenuItem(
onPressed: () {
showDialog(
context: context, builder: (context) => UserProfilePicEditor(u));
},
icon: const Icon(FeatherIcons.camera),
title: Text("edit_profile_picture".i18n),
);
}
}
// ignore: must_be_immutable
class UserProfilePicEditor extends StatefulWidget {
late User u;
UserProfilePicEditor(this.u, {Key? key}) : super(key: key);
@override
State<UserProfilePicEditor> createState() => _UserProfilePicEditorState();
}
class _UserProfilePicEditorState extends State<UserProfilePicEditor> {
late final UserProvider user;
final cropKey = GlobalKey<CropState>();
File? _file;
File? _sample;
File? _lastCropped;
File? image;
Future pickImage() async {
try {
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
File imageFile = File(image.path);
final sample = await ImageCrop.sampleImage(
file: imageFile,
preferredSize: context.size!.longestSide.ceil(),
);
_sample?.delete();
_file?.delete();
setState(() {
_sample = sample;
_file = imageFile;
});
} on PlatformException catch (e) {
log('Failed to pick image: $e');
}
}
Widget cropImageWidget() {
return SizedBox(
height: 300,
child: Crop.file(
_sample!,
key: cropKey,
aspectRatio: 1.0,
),
);
}
Widget openImageWidget() {
return InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14.0),
),
onTap: () => pickImage(),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(14.0),
),
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 8.0),
child: Column(
children: [
Text(
"click_here".i18n,
style: const TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.w600,
),
),
Text(
"select_profile_picture".i18n,
style: const TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w500,
),
)
],
),
),
);
}
Future<void> _cropImage() async {
final scale = cropKey.currentState!.scale;
final area = cropKey.currentState!.area;
if (area == null || _file == null) {
return;
}
final sample = await ImageCrop.sampleImage(
file: _file!,
preferredSize: (2000 / scale).round(),
);
final file = await ImageCrop.cropImage(
file: sample,
area: area,
);
sample.delete();
_lastCropped?.delete();
_lastCropped = file;
List<int> imageBytes = await _lastCropped!.readAsBytes();
String base64Image = base64Encode(imageBytes);
widget.u.picture = base64Image;
Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(widget.u);
Provider.of<UserProvider>(context, listen: false).refresh();
debugPrint('$file');
}
@override
void initState() {
super.initState();
user = Provider.of<UserProvider>(context, listen: false);
}
@override
void dispose() {
super.dispose();
_file?.delete();
_sample?.delete();
_lastCropped?.delete();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
contentPadding: const EdgeInsets.only(top: 10.0),
title: Text("edit_profile_picture".i18n),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: _sample == null ? openImageWidget() : cropImageWidget(),
),
if (widget.u.picture != "")
TextButton(
child: Text(
"remove_profile_picture".i18n,
style: const TextStyle(
fontWeight: FontWeight.w500, color: Colors.red),
),
onPressed: () {
widget.u.picture = "";
Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(widget.u);
Provider.of<UserProvider>(context, listen: false).refresh();
Navigator.of(context).pop(true);
},
),
],
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"done".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () async {
await _cropImage();
Navigator.of(context).pop(true);
},
),
],
);
}
}

View File

@@ -1,977 +0,0 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/shared_theme.dart';
import 'package:filcnaplo/theme/colors/accent.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/theme/observer.dart';
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/filter_bar.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_tile.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/providers/share_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/colorpicker.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'theme.i18n.dart';
import 'package:share_plus/share_plus.dart';
class PremiumCustomAccentColorSetting extends StatefulWidget {
const PremiumCustomAccentColorSetting({Key? key}) : super(key: key);
@override
State<PremiumCustomAccentColorSetting> createState() =>
_PremiumCustomAccentColorSettingState();
}
enum CustomColorMode {
theme,
saved,
accent,
background,
highlight,
icon,
enterId,
}
class _PremiumCustomAccentColorSettingState
extends State<PremiumCustomAccentColorSetting>
with TickerProviderStateMixin {
late final SettingsProvider settings;
late final ShareProvider shareProvider;
bool colorSelection = false;
bool customColorMenu = false;
CustomColorMode colorMode = CustomColorMode.theme;
final customColorInput = TextEditingController();
final unknownColor = Colors.black;
late TabController _testTabController;
late TabController _colorsTabController;
late AnimationController _openAnimController;
late final Animation<double> backgroundAnimation =
Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.2, 1.0, curve: Curves.easeInOut),
),
);
late final Animation<double> fullPageAnimation =
Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.0, 0.6, curve: Curves.easeInOut),
),
);
late final Animation<double> backContainerAnimation =
Tween<double>(begin: 100, end: 0).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.0, 0.9, curve: Curves.easeInOut),
),
);
late final Animation<double> backContentAnimation =
Tween<double>(begin: 100, end: 0).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.2, 1.0, curve: Curves.easeInOut),
),
);
late final Animation<double> backContentScaleAnimation =
Tween<double>(begin: 0.8, end: 0.9).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.45, 1.0, curve: Curves.easeInOut),
),
);
late final Animation<double> pickerContainerAnimation =
Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _openAnimController,
curve: const Interval(0.25, 0.8, curve: Curves.easeInOut),
),
);
@override
void initState() {
super.initState();
_colorsTabController = TabController(length: 5, vsync: this);
_testTabController = TabController(length: 4, vsync: this);
settings = Provider.of<SettingsProvider>(context, listen: false);
shareProvider = Provider.of<ShareProvider>(context, listen: false);
_openAnimController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 750));
_openAnimController.forward();
}
@override
void dispose() {
_openAnimController.dispose();
super.dispose();
}
void setTheme(ThemeMode mode, bool store) async {
await settings.update(theme: mode, store: store);
Provider.of<ThemeModeObserver>(context, listen: false)
.changeTheme(mode, updateNavbarColor: false);
}
dynamic getCustomColor() {
switch (colorMode) {
case CustomColorMode.theme:
return accentColorMap[settings.accentColor];
case CustomColorMode.saved:
return [
settings.customBackgroundColor,
settings.customHighlightColor,
settings.customAccentColor
];
case CustomColorMode.background:
return settings.customBackgroundColor;
case CustomColorMode.highlight:
return settings.customHighlightColor;
case CustomColorMode.accent:
return settings.customAccentColor;
case CustomColorMode.icon:
return settings.customIconColor;
case CustomColorMode.enterId:
// do nothing here lol
break;
}
}
void updateCustomColor(dynamic v, bool store,
{Color? accent, Color? background, Color? panels, Color? icon}) {
if (colorMode != CustomColorMode.theme) {
settings.update(accentColor: AccentColor.custom, store: store);
}
switch (colorMode) {
case CustomColorMode.theme:
settings.update(
accentColor: accentColorMap.keys.firstWhere(
(element) => accentColorMap[element] == v,
orElse: () => AccentColor.filc),
store: store);
settings.update(
customBackgroundColor: AppColors.of(context).background,
store: store);
settings.update(
customHighlightColor: AppColors.of(context).highlight,
store: store);
settings.update(customAccentColor: v, store: store);
break;
case CustomColorMode.saved:
settings.update(customBackgroundColor: v[0], store: store);
settings.update(customHighlightColor: v[1], store: store);
settings.update(customAccentColor: v[3], store: store);
break;
case CustomColorMode.background:
settings.update(customBackgroundColor: v, store: store);
break;
case CustomColorMode.highlight:
settings.update(customHighlightColor: v, store: store);
break;
case CustomColorMode.accent:
settings.update(customAccentColor: v, store: store);
break;
case CustomColorMode.icon:
settings.update(customIconColor: v, store: store);
break;
case CustomColorMode.enterId:
settings.update(customBackgroundColor: background, store: store);
settings.update(customHighlightColor: panels, store: store);
settings.update(customAccentColor: accent, store: store);
settings.update(customIconColor: icon, store: store);
break;
}
}
@override
Widget build(BuildContext context) {
bool hasAccess = Provider.of<PremiumProvider>(context)
.hasScope(PremiumScopes.customColors);
bool isBackgroundDifferent = Theme.of(context).colorScheme.background !=
AppColors.of(context).background;
ThemeMode currentTheme = Theme.of(context).brightness == Brightness.light
? ThemeMode.light
: ThemeMode.dark;
return WillPopScope(
onWillPop: () async {
Provider.of<ThemeModeObserver>(context, listen: false)
.changeTheme(settings.theme, updateNavbarColor: true);
return true;
},
child: AnimatedBuilder(
animation: _openAnimController,
builder: (context, child) {
final backgroundGradientBottomColor = isBackgroundDifferent
? Theme.of(context).colorScheme.background
: HSVColor.fromColor(Theme.of(context).colorScheme.background)
.withValue(currentTheme == ThemeMode.dark
? 0.1 * _openAnimController.value
: 1.0 - (0.1 * _openAnimController.value))
.withAlpha(1.0)
.toColor();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: backgroundGradientBottomColor,
));
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [0.0, 0.75],
colors: isBackgroundDifferent
? [
Theme.of(context).colorScheme.background.withOpacity(1 -
((currentTheme == ThemeMode.dark ? 0.65 : 0.45) *
backgroundAnimation.value)),
backgroundGradientBottomColor,
]
: [
backgroundGradientBottomColor,
backgroundGradientBottomColor
],
),
),
child: Opacity(
opacity: fullPageAnimation.value,
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
actions: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: IconButton(
focusColor: Colors.transparent,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
onPressed: () async {
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(
// duration: Duration(milliseconds: 1000),
// content: Text(
// "Hamarosan...",
// ),
// ),
// );
showDialog(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("share_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () async {
Navigator.of(context).pop();
SharedGradeColors gradeColors =
await shareProvider
.shareCurrentGradeColors(context);
SharedTheme theme =
await shareProvider.shareCurrentTheme(
context,
gradeColors: gradeColors,
);
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
},
),
],
),
),
);
},
icon: const Icon(
FeatherIcons.share2,
size: 22.0,
),
),
),
],
title: Text(
"theme_prev".i18n,
style: TextStyle(color: AppColors.of(context).text),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Stack(
children: [
Opacity(
opacity: 1 - backContainerAnimation.value * (1 / 100),
child: Transform.translate(
offset: Offset(0, backContainerAnimation.value),
child: Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: LinearGradient(
// https://discord.com/channels/1111649116020285532/1153619667848548452
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [
0.35,
0.75
],
colors: [
settings.customBackgroundColor ??
Theme.of(context).colorScheme.background,
isBackgroundDifferent
? HSVColor.fromColor(Theme.of(context)
.colorScheme
.background)
.withSaturation((HSVColor.fromColor(
Theme.of(context)
.colorScheme
.background)
.saturation -
0.15)
.clamp(0.0, 1.0))
.toColor()
: backgroundGradientBottomColor,
]),
),
margin: const EdgeInsets.symmetric(
vertical: 30, horizontal: 20),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Opacity(
opacity: 1 - backContentAnimation.value * (1 / 100),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Transform.translate(
offset:
Offset(0, -24 + backContentAnimation.value),
child: Transform.scale(
scale: backContentScaleAnimation.value,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32.0, vertical: 6.0),
child: FilterBar(
items: const [
Tab(text: "All"),
Tab(text: "Grades"),
Tab(text: "Messages"),
Tab(text: "Absences"),
],
controller: _testTabController,
padding: EdgeInsets.zero,
censored: true,
disableFading: true,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18.0, vertical: 8.0),
child: NewGradesSurprise(
[
Grade.fromJson(
{
"Uid": "0,Ertekeles",
"RogzitesDatuma":
"2022-01-01T23:00:00Z",
"KeszitesDatuma":
"2022-01-01T23:00:00Z",
"LattamozasDatuma": null,
"Tantargy": {
"Uid": "0",
"Nev": "reFilc szakirodalom",
"Kategoria": {
"Uid": "0,_",
"Nev": "_",
"Leiras": "Nem mondom meg"
},
"SortIndex": 2
},
"Tema":
"Kupak csomag vásárlás vizsga",
"Tipus": {
"Uid": "0,_",
"Nev": "_",
"Leiras":
"Évközi jegy/értékelés",
},
"Mod": {
"Uid": "0,_",
"Nev": "_",
"Leiras": "_ feladat",
},
"ErtekFajta": {
"Uid": "1,Osztalyzat",
"Nev": "Osztalyzat",
"Leiras":
"Elégtelen (1) és Jeles (5) között az öt alapértelmezett érték"
},
"ErtekeloTanarNeve": "Premium",
"Jelleg": "Ertekeles",
"SzamErtek": 5,
"SzovegesErtek": "Jeles(5)",
"SulySzazalekErteke": 100,
"SzovegesErtekelesRovidNev":
null,
"OsztalyCsoport": {"Uid": "0"},
"SortIndex": 2
},
),
],
censored: true,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 6.0),
child: Panel(
child: GradeTile(
Grade.fromJson(
{
"Uid": "0,Ertekeles",
"RogzitesDatuma":
"2022-01-01T23:00:00Z",
"KeszitesDatuma":
"2022-01-01T23:00:00Z",
"LattamozasDatuma": null,
"Tantargy": {
"Uid": "0",
"Nev": "reFilc szakosztály",
"Kategoria": {
"Uid": "0,_",
"Nev": "_",
"Leiras": "Nem mondom meg"
},
"SortIndex": 2
},
"Tema":
"Kupak csomag vásárlás vizsga",
"Tipus": {
"Uid": "0,_",
"Nev": "_",
"Leiras":
"Évközi jegy/értékelés",
},
"Mod": {
"Uid": "0,_",
"Nev": "_",
"Leiras": "_ feladat",
},
"ErtekFajta": {
"Uid": "1,Osztalyzat",
"Nev": "Osztalyzat",
"Leiras":
"Elégtelen (1) és Jeles (5) között az öt alapértelmezett érték"
},
"ErtekeloTanarNeve": "Premium",
"Jelleg": "Ertekeles",
"SzamErtek": 5,
"SzovegesErtek": "Jeles(5)",
"SulySzazalekErteke": 100,
"SzovegesErtekelesRovidNev":
null,
"OsztalyCsoport": {"Uid": "0"},
"SortIndex": 2
},
),
padding: EdgeInsets.zero,
censored: true,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 6.0),
child: Panel(
child: HomeworkTile(
Homework.fromJson(
{
"Uid": "0",
"Tantargy": {
"Uid": "0",
"Nev":
"reFilc premium előnyei",
"Kategoria": {
"Uid": "0,_",
"Nev": "_",
"Leiras":
"reFilc premium előnyei",
},
"SortIndex": 0
},
"TantargyNeve":
"reFilc premium előnyei",
"RogzitoTanarNeve":
"Kupak János",
"Szoveg":
"45 perc filctollal való rajzolás",
"FeladasDatuma":
"2022-01-01T23:00:00Z",
"HataridoDatuma":
"2022-01-01T23:00:00Z",
"RogzitesIdopontja":
"2022-01-01T23:00:00Z",
"IsTanarRogzitette": true,
"IsTanuloHaziFeladatEnabled":
false,
"IsMegoldva": false,
"IsBeadhato": false,
"OsztalyCsoport": {"Uid": "0"},
"IsCsatolasEngedelyezes": false
},
),
padding: EdgeInsets.zero,
censored: true,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 6.0),
child: Panel(
child: MessageTile(
Message.fromJson(
{
"azonosito": 0,
"isElolvasva": true,
"isToroltElem": false,
"tipus": {
"azonosito": 1,
"kod": "BEERKEZETT",
"rovidNev":
"Beérkezett üzenet",
"nev": "Beérkezett üzenet",
"leiras": "Beérkezett üzenet"
},
"uzenet": {
"azonosito": 0,
"kuldesDatum":
"2022-01-01T23:00:00",
"feladoNev": "reFilc",
"feladoTitulus":
"Nagyon magas szintű személy",
"szoveg":
"<p>Kedves Felhasználó!</p><p><br></p><p>A prémium vásárlásakor kapott filctollal 90%-al több esély van jó jegyek szerzésére.</p>",
"targy":
"Filctoll használati útmutató",
"statusz": {
"azonosito": 2,
"kod": "KIKULDVE",
"rovidNev": "Kiküldve",
"nev": "Kiküldve",
"leiras": "Kiküldve"
},
"cimzettLista": [
{
"azonosito": 0,
"kretaAzonosito": 0,
"nev": "Tinta Józsi",
"tipus": {
"azonosito": 0,
"kod": "TANULO",
"rovidNev": "Tanuló",
"nev": "Tanuló",
"leiras": "Tanuló"
}
},
],
"csatolmanyok": [
{
"azonosito": 0,
"fajlNev": "Filctoll.doc"
}
]
}
},
),
censored: true,
),
),
),
],
),
),
),
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Wrap(
children: [
Opacity(
opacity: pickerContainerAnimation.value,
child: SizedBox(
width: double.infinity,
child: Container(
padding: const EdgeInsets.only(bottom: 12.0),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: backgroundGradientBottomColor,
offset: const Offset(0, -8),
blurRadius: 16,
spreadRadius: 18,
),
],
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: const [
0.0,
0.175
],
colors: [
backgroundGradientBottomColor,
backgroundGradientBottomColor,
]),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0),
child: FilterBar(
items: [
ColorTab(
color: accentColorMap[
settings.accentColor] ??
unknownColor,
tab: Tab(
text: "colorpicker_presets"
.i18n)),
ColorTab(
color: unknownColor,
tab: Tab(text: "enter_id".i18n)),
/*ColorTab(
color:
settings.customAccentColor ??
unknownColor,
tab: Tab(
text: "colorpicker_saved"
.i18n)),*/
ColorTab(
unlocked: hasAccess,
color: settings
.customBackgroundColor ??
unknownColor,
tab: Tab(
text: "colorpicker_background"
.i18n)),
ColorTab(
unlocked: hasAccess,
color: settings
.customHighlightColor ??
unknownColor,
tab: Tab(
text: "colorpicker_panels"
.i18n)),
ColorTab(
unlocked: hasAccess,
color:
settings.customAccentColor ??
unknownColor,
tab: Tab(
text: "colorpicker_accent"
.i18n)),
// ColorTab(
// unlocked: hasAccess,
// color: settings.customIconColor ??
// unknownColor,
// tab: Tab(
// text:
// "colorpicker_icon".i18n)),
],
onTap: (index) {
if (!hasAccess) {
index = 0;
_colorsTabController.animateTo(0,
duration: Duration.zero);
PremiumLockedFeatureUpsell.show(
context: context,
feature: PremiumFeature
.customcolors);
}
switch (index) {
case 0:
setState(() {
colorMode =
CustomColorMode.theme;
});
break;
case 1:
setState(() {
colorMode =
CustomColorMode.enterId;
});
break;
/*case 1:
setState(() {
colorMode =
CustomColorMode.saved;
});
break;*/
case 2:
setState(() {
colorMode =
CustomColorMode.background;
});
break;
case 3:
setState(() {
colorMode =
CustomColorMode.highlight;
});
break;
case 4:
setState(() {
colorMode =
CustomColorMode.accent;
});
break;
case 5:
setState(() {
colorMode =
CustomColorMode.icon;
});
break;
}
},
controller: _colorsTabController,
padding: EdgeInsets.zero,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0),
child: SafeArea(
child: FilcColorPicker(
colorMode: colorMode,
pickerColor: colorMode ==
CustomColorMode.accent
? settings.customAccentColor ??
unknownColor
: colorMode ==
CustomColorMode.background
? settings
.customBackgroundColor ??
unknownColor
: colorMode ==
CustomColorMode.theme
? (accentColorMap[settings
.accentColor] ??
AppColors.of(context)
.text) // idk what else
: colorMode ==
CustomColorMode
.highlight
? settings
.customHighlightColor ??
unknownColor
: settings
.customIconColor ??
unknownColor,
onColorChanged: (c) {
setState(() {
updateCustomColor(c, false);
});
setTheme(settings.theme, false);
},
onColorChangeEnd: (c, {adaptive}) {
setState(() {
if (adaptive == true) {
settings.update(
accentColor:
AccentColor.adaptive);
settings.update(
customBackgroundColor:
AppColors.of(context)
.background,
store: true);
settings.update(
customHighlightColor:
AppColors.of(context)
.highlight,
store: true);
settings.update(
customIconColor:
const Color(0x00000000),
store: true);
} else {
updateCustomColor(c, true);
}
});
setTheme(settings.theme, true);
},
onThemeIdProvided: (theme) {
// changing grade colors
List<Color> colors = [
theme.gradeColors.oneColor,
theme.gradeColors.twoColor,
theme.gradeColors.threeColor,
theme.gradeColors.fourColor,
theme.gradeColors.fiveColor,
];
settings.update(
gradeColors: colors);
// changing shadow effect
settings.update(
shadowEffect:
theme.shadowEffect);
// changing theme
setState(() {
updateCustomColor(
null,
true,
accent: theme.accentColor,
background:
theme.backgroundColor,
panels: theme.panelsColor,
icon: theme.iconColor,
);
});
setTheme(settings.theme, true);
},
),
),
),
],
),
),
),
),
],
),
),
],
),
),
),
);
},
),
);
}
}
class ColorTab extends StatelessWidget {
const ColorTab(
{Key? key, required this.tab, required this.color, this.unlocked = true})
: super(key: key);
final Tab tab;
final Color color;
final bool unlocked;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Transform.translate(
offset: const Offset(-3, 1),
child: unlocked
? Container(
width: 15,
height: 15,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
border: Border.all(color: Colors.black, width: 2.0),
),
)
: const Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
child: Icon(Icons.lock,
color: Color.fromARGB(255, 82, 82, 82), size: 18),
),
),
tab
],
);
}
}
class PremiumColorPickerItem extends StatelessWidget {
const PremiumColorPickerItem(
{Key? key, required this.label, this.onTap, required this.color})
: super(key: key);
final String label;
final void Function()? onTap;
final Color color;
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
child: InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
label,
style: TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.w500),
),
),
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: color, shape: BoxShape.circle, border: Border.all()),
),
],
),
),
onTap: onTap,
),
);
}
}

View File

@@ -1,67 +0,0 @@
import 'package:i18n_extension/i18n_extension.dart';
extension SettingsLocalization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"theme_prev": "Preview",
"colorpicker_presets": "Presets",
"colorpicker_background": "Background",
"colorpicker_panels": "Panels",
"colorpicker_accent": "Accent",
"colorpicker_icon": "Icon",
"need_sub": "You need Kupak subscription to use modify this.",
"advanced": "Advanced",
"enter_id": "Enter ID",
"theme_id": "Theme ID...",
"theme_not_found": "Theme not found!",
"attention": "Attention!",
"share_disclaimer":
"By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.",
"understand": "I understand",
"share_subj_theme": "Share Theme",
},
"hu_hu": {
"theme_prev": "Előnézet",
"colorpicker_presets": "Téma",
"colorpicker_background": "Háttér",
"colorpicker_panels": "Panelek",
"colorpicker_accent": "Színtónus",
"colorpicker_icon": "Ikon",
"need_sub": "A módosításhoz Kupak szintű támogatás szükséges.",
"advanced": "Haladó",
"enter_id": "ID megadása",
"theme_id": "Téma azonosító...",
"theme_not_found": "A téma nem található!",
"attention": "Figyelem!",
"share_disclaimer":
"A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.",
"understand": "Értem",
"share_subj_theme": "Téma Megosztás",
},
"de_de": {
"theme_prev": "Vorschau",
"colorpicker_presets": "Farben",
"colorpicker_background": "Hintergrund",
"colorpicker_panels": "Tafeln",
"colorpicker_accent": "Akzent",
"colorpicker_icon": "Ikone",
"need_sub":
"Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.",
"advanced": "Fortschrittlich",
"enter_id": "Geben Sie die ID ein",
"theme_id": "Themen-ID...",
"theme_not_found": "Thema nicht gefunden!",
"attention": "Achtung!",
"share_disclaimer":
"Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.",
"understand": "Ich verstehe",
"share_subj_theme": "Thema Teilen",
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -1,218 +0,0 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart';
import 'package:filcnaplo_mobile_ui/common/empty.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo/utils/format.dart';
import 'dart:math' as math;
import 'package:intl/intl.dart';
import 'package:i18n_extension/i18n_widget.dart';
import 'package:provider/provider.dart';
class PremiumFSTimetable extends StatefulWidget {
const PremiumFSTimetable({Key? key, required this.controller})
: super(key: key);
final TimetableController controller;
@override
State<PremiumFSTimetable> createState() => _PremiumFSTimetableState();
}
class _PremiumFSTimetableState extends State<PremiumFSTimetable> {
late SettingsProvider settings;
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
));
}
@override
Widget build(BuildContext context) {
settings = Provider.of<SettingsProvider>(context);
if (widget.controller.days == null || widget.controller.days!.isEmpty) {
return const Center(child: Empty());
}
final days = widget.controller.days!;
final everyLesson = days.expand((x) => x).toList();
everyLesson.sort((a, b) => a.start.compareTo(b.start));
final int maxLessonCount = days.fold(
0,
(a, b) => math.max(
a, b.where((l) => l.subject.id != "" || l.isEmpty).length));
const prefixw = 40;
const padding = prefixw + 6 * 2;
final colw = (MediaQuery.of(context).size.width - padding) / days.length;
return Scaffold(
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
shadowColor: Colors.transparent,
),
body: ListView.builder(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 24.0),
itemCount: maxLessonCount + 1,
itemBuilder: (context, index) {
List<Widget> columns = [];
for (int dayIndex = -1; dayIndex < days.length; dayIndex++) {
if (dayIndex == -1) {
if (index >= 1) {
columns.add(SizedBox(
width: prefixw.toDouble(),
height: 40.0,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
(index).toString()+".",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.secondary),
),
),
));
} else {
columns.add(SizedBox(width: prefixw.toDouble()));
}
continue;
}
final lessons = days[dayIndex]
.where((l) => l.subject.id != "" || l.isEmpty)
.toList();
if (lessons.isEmpty) continue;
final dayOffset = int.tryParse(lessons.first.lessonIndex) ?? 0;
if (index == 0 && dayIndex >= 0) {
columns.add(
SizedBox(
width: colw,
height: 40.0,
child: Text(
DateFormat("EEEE", I18n.of(context).locale.languageCode)
.format(lessons.first.date)
.capital(),
style: const TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
),
);
continue;
}
final lessonIndex = index - dayOffset;
if (lessonIndex < 0 || lessonIndex >= lessons.length) {
columns.add(SizedBox(width: colw));
continue;
}
if (lessons[lessonIndex].isEmpty) {
columns.add(
SizedBox(
width: colw,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
FeatherIcons.slash,
size: 18.0,
color: AppColors.of(context).text.withOpacity(.3),
),
const SizedBox(width: 8.0),
Text(
"Lyukas óra",
style: TextStyle(
color: AppColors.of(context).text.withOpacity(.3),
),
),
],
),
),
);
continue;
}
columns.add(
SizedBox(
width: colw,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
SubjectIcon.resolveVariant(
context: context,
subject: lessons[lessonIndex].subject,
),
size: 18.0,
color: AppColors.of(context).text.withOpacity(.7),
),
const SizedBox(width: 8.0),
Expanded(
child: Text(
lessons[lessonIndex].subject.renamedTo ??
lessons[lessonIndex].subject.name.capital(),
maxLines: 1,
style: TextStyle(
fontStyle: lessons[lessonIndex]
.subject
.isRenamed && settings.renamedSubjectsItalics
? FontStyle.italic
: null,
),
overflow: TextOverflow.clip,
softWrap: false,
),
),
const SizedBox(width: 15),
],
),
Padding(
padding: const EdgeInsets.only(left: 26.0),
child: Text(
lessons[lessonIndex].room,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(.5),
overflow: TextOverflow.ellipsis,
),
),
),
],
),
),
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: columns,
);
},
),
);
}
}

View File

@@ -1,62 +0,0 @@
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart';
import 'package:filcnaplo_mobile_ui/common/system_chrome.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:filcnaplo_premium/ui/mobile/timetable/fs_timetable.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.i18n.dart';
class PremiumFSTimetableButton extends StatelessWidget {
const PremiumFSTimetableButton(
{Key? key, required this.controller, required this.tabcontroller})
: super(key: key);
final TimetableController controller;
final TabController tabcontroller;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(
splashRadius: 24.0,
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.fsTimetable)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.weeklytimetable);
return;
}
// If timetable empty, show empty
if (tabcontroller.length == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("empty_timetable".i18n),
duration: const Duration(seconds: 2),
));
return;
}
Navigator.of(context, rootNavigator: true)
.push(PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
PremiumFSTimetable(
controller: controller,
),
))
.then((_) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp]);
setSystemChrome(context);
});
},
icon: Icon(FeatherIcons.trello, color: AppColors.of(context).text),
),
);
}
}