From b626b05a1a6760f2895dc027b472125441cf52e7 Mon Sep 17 00:00:00 2001 From: Kherel Date: Mon, 30 Nov 2020 11:03:55 +0100 Subject: [PATCH] update first page --- .vscode/launch.json | 13 ++ assets/images/logos/aws.png | Bin 0 -> 1844 bytes assets/images/logos/cloudflare.png | Bin 0 -> 1595 bytes assets/images/logos/hetzner.png | Bin 0 -> 1265 bytes assets/images/logos/namecheap.png | Bin 0 -> 2207 bytes ios/Podfile.lock | 6 + lib/config/brand_colors.dart | 8 +- lib/config/brand_shadow.dart | 7 + lib/config/brand_theme.dart | 24 +- lib/config/text_themes.dart | 35 +++ lib/main.dart | 1 + .../components/brand_button/brand_button.dart | 59 ++++- lib/ui/components/brand_card/brand_card.dart | 25 ++ .../brand_modal_sheet/brand_modal_sheet.dart | 41 ++++ .../brand_span_button/brand_span_button.dart | 37 +++ lib/ui/pages/onboarding/onboarding.dart | 7 +- lib/ui/pages/rootRoute.dart | 9 +- lib/ui/pages/servers/servers.dart | 218 +++++++++++++++++- lib/utils/extensions/elevation_extension.dart | 33 +++ lib/utils/extensions/text_extension.dart | 43 ++++ pubspec.lock | 49 +++- pubspec.yaml | 37 +-- 22 files changed, 588 insertions(+), 64 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 assets/images/logos/aws.png create mode 100644 assets/images/logos/cloudflare.png create mode 100644 assets/images/logos/hetzner.png create mode 100644 assets/images/logos/namecheap.png create mode 100644 lib/config/brand_shadow.dart create mode 100644 lib/config/text_themes.dart create mode 100644 lib/ui/components/brand_card/brand_card.dart create mode 100644 lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart create mode 100644 lib/ui/components/brand_span_button/brand_span_button.dart create mode 100644 lib/utils/extensions/elevation_extension.dart create mode 100644 lib/utils/extensions/text_extension.dart diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..201d6fc1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "selfprivacy", + "request": "launch", + "type": "dart" + } + ] +} \ No newline at end of file diff --git a/assets/images/logos/aws.png b/assets/images/logos/aws.png new file mode 100644 index 0000000000000000000000000000000000000000..81d58839cf7b70737a2dad371c0f8fc86b1039ec GIT binary patch literal 1844 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 bU|abQgQx2VrguEL00000NkvXXu0mjf!vs<= literal 0 HcmV?d00001 diff --git a/assets/images/logos/namecheap.png b/assets/images/logos/namecheap.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1c6744f44c235f6af7c7a5e5da20ba082a98f4 GIT binary patch literal 2207 zcmV;Q2w?Y#P)FM&`@p8mE>NE0=5|U(nBSZGMwHls@K3FsT74z zu`QrI6}8f1&{|B7y+}sTV=cyQ5hH=crQ%vN$(_mf?Q*5Hxcuc90gTLtSdcrj^XA8! z@64Mek-=av7z_r3!C){L3O>jOcI0l{+!a<9& zWEctACuixI`_De59%>8`P}Z>6)*S3d3hbc6OS6LwqmbytjtVnf881MMx_~-`z6RC7 zfs%r68F@1Ju9IOTWRFx2MEZXYy9q5#>6)#H_8Y(SE(2@ric-P%3?GN2OWKHtT6 znR?SxjY7yZl9IISC=b7>^IJ>#(yQ?CV=m zx@}&X++*8t8ywaNXmt1+4y?*Qwa!u>ev`{B@V6qWT3W8bj*F7JEuq$LluFfHF6SzK z`y=QLSoe5RY-|*7bD5P~z6AXjkiUd{Ea_D9gl@>c%>Z%la&Jy}E_XiUx%}#h!uP-A1FPMN8 zE{nljM}Ovh-w)#iDd&B(tJBWQ=;e7hC&$jH)ef>K`!45c=hS%;>fRXYXM!qyTE19ho3}!xO(-fXf)e*c_P4GC9>72a}JaC`q-HL z1AYcmfA6Th%RKD$9O8+e1<@&%(hQBF!U$HQZ}V%io{{RoFV47_p{~TJ2npx$M|74^ ze#RQC!Xq?&aHAY{J_$HAjU{jas+flTSshy`Q2ohZO#6a1qnin|r$O`y4pEK};v6sd z%h}bNoVV|NfwoGAm%nziIcwwYqywKZtn^0w-nPl}wQ#IvGmR-clfrfY+d*EV>GL?a zSW4VzspUSP&S{JkFV#n58*lHcuL`PpbQ{gS79KJBtBF-uNy_4XE}+J8pYmz(%BflZ ziv!YD=s1b+EqGCbRtsrW$8R3orlHY2m+x$L^^PL_3HFU!`1VphFRx-g%5OBfm;QEx zs!6tjcMg(Dhg#h`nTMv&&|rGD6Snu7*aJ@_uTNOy)oS%MOtx7ib-7Jgp@_~y{nwN1 z@V=eP{npdebnNF=VZ;D6j=%mL=iuWfBrO$O9FYbpL~m?`mvAotR-mu7^IoaA7^N*Q z=jgf7`kwF0^EWn%AHhnI z=1j*8NG18c66LdMF`B#bc~uw@{Eyh@+?bl4A~AW2T8$L7nOs6LVgzNPw9Ujen!@~qN&NJneYDN!3$YRzX|j@hD1m>ssxAe`KcPDXySLH zi=#M@3HHXe&u7U>QvTFy<8 white.withOpacity(0.8); } diff --git a/lib/config/brand_shadow.dart b/lib/config/brand_shadow.dart new file mode 100644 index 00000000..39c97a64 --- /dev/null +++ b/lib/config/brand_shadow.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +final shadow8 = BoxShadow( + offset: Offset(0, 4), + blurRadius: 8, + color: Colors.black.withOpacity(.08), +); diff --git a/lib/config/brand_theme.dart b/lib/config/brand_theme.dart index 1a643c88..e08c09d1 100644 --- a/lib/config/brand_theme.dart +++ b/lib/config/brand_theme.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:selfprivacy/utils/named_font_weight.dart'; +import 'package:selfprivacy/config/text_themes.dart'; import 'brand_colors.dart'; @@ -10,24 +10,10 @@ var theme = ThemeData( scaffoldBackgroundColor: BrandColors.scaffoldBackground, textTheme: GoogleFonts.interTextTheme( TextTheme( - headline1: TextStyle( - fontSize: 40, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, - ), - headline2: TextStyle( - fontSize: 24, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, - ), - caption: TextStyle( - fontSize: 18, - fontWeight: NamedFontWeight.medium, - color: BrandColors.headlineColor), - bodyText1: TextStyle( - fontSize: 15, - color: BrandColors.textColor, - ), + headline1: headline1Style, + headline2: headline2Style, + caption: captionStyle, + bodyText1: bodyText1Style, ), ), ); diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart new file mode 100644 index 00000000..a871d0e8 --- /dev/null +++ b/lib/config/text_themes.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:selfprivacy/utils/named_font_weight.dart'; + +import 'brand_colors.dart'; + +final defaultTextStyle = GoogleFonts.inter( + textStyle: TextStyle( + fontSize: 15, + color: BrandColors.textColor1, + ), +); + +final headline1Style = GoogleFonts.inter( + fontSize: 40, + fontWeight: NamedFontWeight.extraBold, + color: BrandColors.headlineColor, +); + +final headline2Style = GoogleFonts.inter( + fontSize: 24, + fontWeight: NamedFontWeight.extraBold, + color: BrandColors.headlineColor, +); + +final captionStyle = GoogleFonts.inter( + fontSize: 18, + fontWeight: NamedFontWeight.medium, + color: BrandColors.headlineColor, +); + +final bodyText1Style = defaultTextStyle; +final body2TextStyle = defaultTextStyle.copyWith( + color: BrandColors.textColor2, +); diff --git a/lib/main.dart b/lib/main.dart index 16fda33c..039713f7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ void main() { class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + return AnnotatedRegion( value: SystemUiOverlayStyle.light, // Manually changnig appbar color child: MaterialApp( diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index bb475669..273c04ce 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; -enum BrandButtonTypes { rised } +enum BrandButtonTypes { rised, text } class BrandButton extends StatelessWidget { const BrandButton({ @@ -19,8 +19,8 @@ class BrandButton extends StatelessWidget { static rised({ Key key, - VoidCallback onPressed, - String title, + @required VoidCallback onPressed, + @required String title, }) => BrandButton( key: key, @@ -29,6 +29,18 @@ class BrandButton extends StatelessWidget { type: BrandButtonTypes.rised, ); + static text({ + Key key, + @required VoidCallback onPressed, + @required String title, + }) => + BrandButton( + key: key, + onPressed: onPressed, + title: title, + type: BrandButtonTypes.text, + ); + @override Widget build(BuildContext context) { switch (type) { @@ -37,6 +49,12 @@ class BrandButton extends StatelessWidget { title: title, onPressed: onPressed, ); + case BrandButtonTypes.text: + return _TextButton( + title: title, + onPressed: onPressed, + ); + break; } return null; @@ -73,7 +91,7 @@ class _RisedButton extends StatelessWidget { style: TextStyle( color: BrandColors.white, fontSize: 16, - fontWeight: FontWeight.w700, + fontWeight: FontWeight.bold, height: 1.5, ), ), @@ -84,3 +102,36 @@ class _RisedButton extends StatelessWidget { ); } } + +class _TextButton extends StatelessWidget { + const _TextButton({ + Key key, + this.onPressed, + this.title, + }) : super(key: key); + + final VoidCallback onPressed; + final String title; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onPressed, + child: Container( + height: 48, + width: double.infinity, + alignment: Alignment.center, + padding: EdgeInsets.all(12), + child: Text( + title, + style: TextStyle( + color: BrandColors.blue, + fontSize: 16, + fontWeight: FontWeight.bold, + height: 1.5, + ), + ), + ), + ); + } +} diff --git a/lib/ui/components/brand_card/brand_card.dart b/lib/ui/components/brand_card/brand_card.dart new file mode 100644 index 00000000..67d741d0 --- /dev/null +++ b/lib/ui/components/brand_card/brand_card.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/utils/extensions/elevation_extension.dart'; + +class BrandCard extends StatelessWidget { + const BrandCard({Key key, this.child}) : super(key: key); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(bottom: 30), + decoration: BoxDecoration( + color: BrandColors.white, + borderRadius: BorderRadius.circular(20), + ).ev8, + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 15, + ), + child: child, + ); + } +} diff --git a/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart b/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart new file mode 100644 index 00000000..fb5ed468 --- /dev/null +++ b/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; + +class BrandModalSheet extends StatelessWidget { + const BrandModalSheet({ + Key key, + this.child, + }) : super(key: key); + + final Widget child; + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 32, bottom: 6), + child: Container( + height: 4, + width: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + color: Color(0xFFE3E3E3).withOpacity(0.65), + ), + ), + ), + Container( + constraints: BoxConstraints(minHeight: 400), + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + color: BrandColors.white, + ), + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 40, horizontal: 15), + child: child, + ), + ], + ), + ); + } +} diff --git a/lib/ui/components/brand_span_button/brand_span_button.dart b/lib/ui/components/brand_span_button/brand_span_button.dart new file mode 100644 index 00000000..ac7c53db --- /dev/null +++ b/lib/ui/components/brand_span_button/brand_span_button.dart @@ -0,0 +1,37 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class BrandSpanButton extends TextSpan { + BrandSpanButton({ + @required String text, + @required VoidCallback onTap, + TextStyle style, + }) : assert(text != null), + assert(onTap != null), + super( + recognizer: TapGestureRecognizer()..onTap = onTap, + text: text, + style: (style ?? TextStyle()).copyWith(color: BrandColors.blue), + ); + + static link({ + @required String text, + String urlString, + TextStyle style, + }) => + BrandSpanButton( + text: text, + style: style, + onTap: () => _launchURL(urlString ?? text), + ); + + static _launchURL(String link) async { + if (await canLaunch(link)) { + await launch(link); + } else { + throw 'Could not launch $link'; + } + } +} diff --git a/lib/ui/pages/onboarding/onboarding.dart b/lib/ui/pages/onboarding/onboarding.dart index e9eaabd5..0838ba65 100644 --- a/lib/ui/pages/onboarding/onboarding.dart +++ b/lib/ui/pages/onboarding/onboarding.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:selfprivacy/utils/extensions/text_extension.dart'; class OnboardingPage extends StatelessWidget { const OnboardingPage({Key key}) : super(key: key); @@ -27,13 +28,11 @@ class OnboardingPage extends StatelessWidget { children: [ Text( 'Онбординг', - style: Theme.of(context).textTheme.headline1, - ), + ).h1, SizedBox(height: 20), Text( 'Тут рассказ на 1-2 слайда о том, что делает это приложение, какие твои проблемы решает и как (в общем чего ожидать от сервиса).', - style: Theme.of(context).textTheme.bodyText1, - ), + ).body2, ], ), ), diff --git a/lib/ui/pages/rootRoute.dart b/lib/ui/pages/rootRoute.dart index dbaea3d6..a7cfaa82 100644 --- a/lib/ui/pages/rootRoute.dart +++ b/lib/ui/pages/rootRoute.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart'; +import 'package:selfprivacy/ui/pages/servers/servers.dart'; class RootPage extends StatefulWidget { const RootPage({Key key}) : super(key: key); @@ -32,10 +33,10 @@ class _RootPageState extends State body: TabBarView( controller: tabController, children: [ - Text('a'), - Text('b'), - Text('c'), - Text('d'), + ServersPage(), + Text('services'), + Text('users'), + Text('more'), ], ), bottomNavigationBar: BottomTabBar( diff --git a/lib/ui/pages/servers/servers.dart b/lib/ui/pages/servers/servers.dart index c3be1224..924ba6b3 100644 --- a/lib/ui/pages/servers/servers.dart +++ b/lib/ui/pages/servers/servers.dart @@ -1,4 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; +import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; +import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; +import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; +import 'package:selfprivacy/utils/extensions/text_extension.dart'; class ServersPage extends StatelessWidget { const ServersPage({Key key}) : super(key: key); @@ -7,8 +14,217 @@ class ServersPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: Container( - child: Text('aaa111'), + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 15, vertical: 30), + children: [ + Text('Начало').caption, + Text('SelfPrivacy').h1, + SizedBox( + height: 10, + ), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: + 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', + style: body2TextStyle, + ), + BrandSpanButton.link( + text: 'selfprivacy.org/start', + urlString: 'https://selfprivacy.org/start', + ), + ], + ), + ), + SizedBox(height: 50), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/hetzner.png'), + SizedBox(height: 10), + Text('1. Подключите сервер Hetzner').h2, + SizedBox(height: 10), + Text('Здесь будут жить наши данные и SelfPrivacy-сервисы') + .body2, + _MockForm( + hintText: 'Hetzner API Token', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return BrandModalSheet( + child: Column( + children: [ + Text('Как получить Hetzner API Token').h2, + SizedBox(height: 20), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '1 Переходим по ссылке ', + style: bodyText1Style, + ), + BrandSpanButton.link( + text: 'hetzner.com/sdfsdfsdfsdf', + urlString: + 'https://hetzner.com/sdfsdfsdfsdf', + ), + TextSpan( + text: ''' + +2 Заходим в созданный нами проект. Если такового - нет, значит создаём. + +3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). + +4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. + +5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. + +6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. + + ''', + style: bodyText1Style, + ), + ], + ), + ), + ], + ), + ); + }, + ), + title: 'Как получить API Token', + ), + ], + ), + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/namecheap.png'), + SizedBox(height: 10), + Text('2. Настройте домен ').h2, + SizedBox(height: 10), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'Зарегистрируйте домен в ', + style: body2TextStyle, + ), + BrandSpanButton.link( + text: 'NameCheap', + urlString: 'https://www.namecheap.com', + ), + TextSpan( + text: + ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', + style: body2TextStyle, + ), + ], + ), + ), + _MockForm( + hintText: 'Домен, например, selfprivacy.org', + submitButtonText: 'Проверить DNS', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как настроить DNS CloudFlare', + ), + ], + ), + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/cloudflare.png'), + SizedBox(height: 10), + Text('3. Подключите CloudFlare DNS').h2, + SizedBox(height: 10), + Text('Для управления DNS вашего домена').body2, + _MockForm( + hintText: 'CloudFlare API Token', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как получить API Token', + ), + ], + ), + ), + BrandCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/aws.png'), + SizedBox(height: 10), + Text('4. Подключите Amazon AWS для бекапа').h2, + SizedBox(height: 10), + Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде') + .body2, + _MockForm( + hintText: 'Amazon AWS Access Key', + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как получить API Token', + ), + ], + ), + ) + ], + ), ), ); } } + +class _MockForm extends StatelessWidget { + const _MockForm({ + Key key, + @required this.hintText, + this.submitButtonText = 'Подключить', + }) : super(key: key); + + final String hintText; + final String submitButtonText; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox(height: 20), + TextField( + style: TextStyle(fontSize: 15, height: 1.6), + decoration: InputDecoration( + hintText: hintText, + contentPadding: EdgeInsets.all(16), + border: InputBorder.none, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + borderSide: BorderSide(color: BrandColors.inputInactive), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + borderSide: BorderSide(color: BrandColors.blue), + ), + ), + ), + SizedBox(height: 20), + BrandButton.rised(onPressed: () {}, title: submitButtonText), + ], + ); + } +} diff --git a/lib/utils/extensions/elevation_extension.dart b/lib/utils/extensions/elevation_extension.dart new file mode 100644 index 00000000..5732c8f9 --- /dev/null +++ b/lib/utils/extensions/elevation_extension.dart @@ -0,0 +1,33 @@ +library elevation_extension; + +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:selfprivacy/config/brand_shadow.dart'; + +extension ElevationExtension on BoxDecoration { + BoxDecoration get ev8 => copyWith(boxShadow: [shadow8]); + + BoxDecoration copyWith({ + Color color, + DecorationImage image, + BoxBorder border, + BorderRadiusGeometry borderRadius, + List boxShadow, + Gradient gradient, + BlendMode backgroundBlendMode, + BoxShape shape, + }) { + return BoxDecoration( + color: color ?? this.color, + image: image ?? this.image, + border: border ?? this.border, + borderRadius: borderRadius ?? this.borderRadius, + boxShadow: this.boxShadow != null || boxShadow != null + ? [...this.boxShadow ?? [], ...boxShadow ?? []] + : null, + gradient: gradient ?? this.gradient, + backgroundBlendMode: backgroundBlendMode ?? this.backgroundBlendMode, + shape: shape ?? this.shape, + ); + } +} diff --git a/lib/utils/extensions/text_extension.dart b/lib/utils/extensions/text_extension.dart new file mode 100644 index 00000000..6094bcf4 --- /dev/null +++ b/lib/utils/extensions/text_extension.dart @@ -0,0 +1,43 @@ +library text_extension; + +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:selfprivacy/config/text_themes.dart'; + +extension TextExtension on Text { + Text get h1 => copyWith(style: headline1Style); + Text get h2 => copyWith(style: headline2Style); + Text get caption => copyWith(style: captionStyle); + + Text get body2 => copyWith(style: body2TextStyle); + + Text setKey(Key key) => copyWith(key: key); + + Text copyWith( + {Key key, + StrutStyle strutStyle, + TextAlign textAlign, + TextDirection textDirection = TextDirection.ltr, + Locale locale, + bool softWrap, + TextOverflow overflow, + double textScaleFactor, + int maxLines, + String semanticsLabel, + TextWidthBasis textWidthBasis, + TextStyle style}) { + return Text(data, + key: key ?? this.key, + strutStyle: strutStyle ?? this.strutStyle, + textAlign: textAlign ?? this.textAlign, + textDirection: textDirection ?? this.textDirection, + locale: locale ?? this.locale, + softWrap: softWrap ?? this.softWrap, + overflow: overflow ?? this.overflow, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + maxLines: maxLines ?? this.maxLines, + semanticsLabel: semanticsLabel ?? this.semanticsLabel, + textWidthBasis: textWidthBasis ?? this.textWidthBasis, + style: style != null ? this.style?.merge(style) ?? style : this.style); + } +} diff --git a/pubspec.lock b/pubspec.lock index 86032e81..3a94cdcc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -95,6 +95,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" google_fonts: dependency: "direct main" description: @@ -261,6 +266,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "5.7.10" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+4" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+9" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.9" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5+1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+3" vector_math: dependency: transitive description: @@ -284,4 +331,4 @@ packages: version: "0.1.2" sdks: dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.17.0 <2.0.0" + flutter: ">=1.22.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index ed8d9c0d..ec9ecece 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: selfprivacy description: selfprivacy.org -publish_to: 'none' +publish_to: 'none' version: 1.0.0+1 environment: @@ -11,39 +11,16 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.0 google_fonts: ^1.1.1 + url_launcher: ^5.7.10 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true - - + assets: + - assets/images/logos/ fonts: - - family: BrandIcons - fonts: - - asset: assets/fonts/BrandIcons.ttf - - - - - - - - - - - - - - - - - - - - - - - - + - family: BrandIcons + fonts: + - asset: assets/fonts/BrandIcons.ttf