add filters
BIN
assets/filters/location/germany.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
assets/filters/location/germany_frankfurt_am_main.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
assets/filters/random/avocardio.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
assets/filters/random/chillen.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
assets/filters/random/duck.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/filters/random/hide_the_pain.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
assets/filters/random/lol.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/filters/random/yolo.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
|
|
@ -32,6 +32,8 @@ class BackgroundLayerData extends Layer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FilterLayerData extends Layer {}
|
||||||
|
|
||||||
/// Attributes used by [EmojiLayer]
|
/// Attributes used by [EmojiLayer]
|
||||||
class EmojiLayerData extends Layer {
|
class EmojiLayerData extends Layer {
|
||||||
String text;
|
String text;
|
||||||
|
|
|
||||||
81
lib/src/components/image_editor/layers/filter_layer.dart
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/data/layer.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filters/datetime_filter.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filters/image_filter.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filters/location_filter.dart';
|
||||||
|
|
||||||
|
/// Main layer
|
||||||
|
class FilterLayer extends StatefulWidget {
|
||||||
|
final FilterLayerData layerData;
|
||||||
|
// final VoidCallback? onUpdate;
|
||||||
|
|
||||||
|
const FilterLayer({
|
||||||
|
super.key,
|
||||||
|
required this.layerData,
|
||||||
|
// this.onUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FilterLayer> createState() => _FilterLayerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilterSceleton extends StatelessWidget {
|
||||||
|
const FilterSceleton({super.key, this.child});
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(child: Container()),
|
||||||
|
if (child != null) child!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilterText extends StatelessWidget {
|
||||||
|
const FilterText(this.text, {super.key, this.fontSize = 24});
|
||||||
|
final String text;
|
||||||
|
final double fontSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: Colors.white,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
color: const Color.fromARGB(122, 0, 0, 0),
|
||||||
|
blurRadius: 5.0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FilterLayerState extends State<FilterLayer> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PageView(
|
||||||
|
children: [
|
||||||
|
FilterSceleton(),
|
||||||
|
DateTimeFilter(),
|
||||||
|
LocationFilter(),
|
||||||
|
ImageFilter(imagePath: "random/lol.png"),
|
||||||
|
ImageFilter(imagePath: "random/hide_the_pain.png"),
|
||||||
|
ImageFilter(imagePath: "random/yolo.png"),
|
||||||
|
ImageFilter(imagePath: "random/chillen.png"),
|
||||||
|
ImageFilter(imagePath: "random/avocardio.png"),
|
||||||
|
ImageFilter(imagePath: "random/duck.png"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filter_layer.dart';
|
||||||
|
|
||||||
|
class DateTimeFilter extends StatelessWidget {
|
||||||
|
const DateTimeFilter({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String currentTime = DateFormat('HH:mm').format(DateTime.now());
|
||||||
|
String currentDate = DateFormat('dd.MM.yyyy').format(DateTime.now());
|
||||||
|
return FilterSceleton(
|
||||||
|
child: Positioned(
|
||||||
|
bottom: 80,
|
||||||
|
left: 40,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
FilterText(currentTime),
|
||||||
|
FilterText(currentDate),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filter_layer.dart';
|
||||||
|
|
||||||
|
class ImageFilter extends StatelessWidget {
|
||||||
|
const ImageFilter({super.key, required this.imagePath});
|
||||||
|
final String imagePath;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FilterSceleton(
|
||||||
|
child: Positioned(
|
||||||
|
bottom: 50,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
child: Center(
|
||||||
|
child: Image.asset(
|
||||||
|
"assets/filters/$imagePath",
|
||||||
|
height: 150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filter_layer.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filters/image_filter.dart';
|
||||||
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
||||||
|
|
||||||
|
class LocationFilter extends StatefulWidget {
|
||||||
|
const LocationFilter({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LocationFilter> createState() => _LocationFilterState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> cities = {
|
||||||
|
"Frankfurt am Main": "germany_frankfurt_am_main.png",
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, String> countries = {
|
||||||
|
"Germany": "germany.png",
|
||||||
|
};
|
||||||
|
|
||||||
|
class _LocationFilterState extends State<LocationFilter> {
|
||||||
|
String? selectedImage;
|
||||||
|
String overlayText = "";
|
||||||
|
Response_Location? location;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future initAsync() async {
|
||||||
|
final res = await apiProvider.getCurrentLocation();
|
||||||
|
if (res.isSuccess) {
|
||||||
|
location = res.value.location;
|
||||||
|
|
||||||
|
if (cities.containsKey(location!.city)) {
|
||||||
|
selectedImage = cities[location!.city];
|
||||||
|
overlayText = location!.city;
|
||||||
|
} else if (countries.containsKey(location!.county)) {
|
||||||
|
selectedImage = countries[location!.county];
|
||||||
|
overlayText = location!.county;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (selectedImage != null) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
ImageFilter(imagePath: "location/${selectedImage!}"),
|
||||||
|
Positioned(
|
||||||
|
bottom: 55,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Center(
|
||||||
|
child: Text(overlayText),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
return FilterSceleton(
|
||||||
|
child: Positioned(
|
||||||
|
bottom: 50,
|
||||||
|
left: 40,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
FilterText(location!.city),
|
||||||
|
FilterText(location!.region),
|
||||||
|
FilterText(location!.county),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:twonly/src/components/image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/components/image_editor/layers/background_layer.dart';
|
import 'package:twonly/src/components/image_editor/layers/background_layer.dart';
|
||||||
import 'package:twonly/src/components/image_editor/layers/draw_layer.dart';
|
import 'package:twonly/src/components/image_editor/layers/draw_layer.dart';
|
||||||
import 'package:twonly/src/components/image_editor/layers/emoji_layer.dart';
|
import 'package:twonly/src/components/image_editor/layers/emoji_layer.dart';
|
||||||
|
import 'package:twonly/src/components/image_editor/layers/filter_layer.dart';
|
||||||
import 'package:twonly/src/components/image_editor/layers/text_layer.dart';
|
import 'package:twonly/src/components/image_editor/layers/text_layer.dart';
|
||||||
|
|
||||||
/// View stacked layers (unbounded height, width)
|
/// View stacked layers (unbounded height, width)
|
||||||
|
|
@ -27,6 +28,11 @@ class LayersViewer extends StatelessWidget {
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
...layers.whereType<FilterLayerData>().map((layerItem) {
|
||||||
|
return FilterLayer(
|
||||||
|
layerData: layerItem,
|
||||||
|
);
|
||||||
|
}),
|
||||||
...layers
|
...layers
|
||||||
.where((layerItem) =>
|
.where((layerItem) =>
|
||||||
layerItem is EmojiLayerData || layerItem is DrawLayerData)
|
layerItem is EmojiLayerData || layerItem is DrawLayerData)
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
|
||||||
// today a message was already send -> update flame
|
// today a message was already send -> update flame
|
||||||
updateFlame = true;
|
updateFlame = true;
|
||||||
}
|
}
|
||||||
} else if (contact.lastMessageReceived!.isAfter(startOfToday)) {
|
} else if (contact.lastMessageReceived != null &&
|
||||||
|
contact.lastMessageReceived!.isAfter(startOfToday)) {
|
||||||
// today a message was already received -> update flame
|
// today a message was already received -> update flame
|
||||||
updateFlame = true;
|
updateFlame = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,13 @@ class ApiProvider {
|
||||||
return await sendRequestSync(req);
|
return await sendRequestSync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Result> getCurrentLocation() async {
|
||||||
|
var get = ApplicationData_GetLocation();
|
||||||
|
var appData = ApplicationData()..getlocation = get;
|
||||||
|
var req = createClientToServerFromApplicationData(appData);
|
||||||
|
return await sendRequestSync(req);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Result> triggerDownload(List<int> token, int offset) async {
|
Future<Result> triggerDownload(List<int> token, int offset) async {
|
||||||
var get = ApplicationData_DownloadData()
|
var get = ApplicationData_DownloadData()
|
||||||
..downloadToken = token
|
..downloadToken = token
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,8 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
image: currentImage,
|
image: currentImage,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
layers.add(FilterLayerData());
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,4 +89,7 @@ flutter:
|
||||||
- assets/images/onboarding/ricky_the_greedy_racoon.png
|
- assets/images/onboarding/ricky_the_greedy_racoon.png
|
||||||
- assets/animated_icons/
|
- assets/animated_icons/
|
||||||
- assets/animations/
|
- assets/animations/
|
||||||
|
- assets/filters/
|
||||||
|
- assets/filters/location/
|
||||||
|
- assets/filters/random/
|
||||||
|
|
||||||
|
|
|
||||||