add dark theme

fdroid
Kherel 2020-12-08 20:26:51 +01:00
parent 7ebfc2c048
commit 9a8c16344f
27 changed files with 798 additions and 512 deletions

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
@ -10,8 +12,15 @@ class BlocAndProviderConfig extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var platformBrightness =
SchedulerBinding.instance.window.platformBrightness;
// var platformBrightness = Brightness.dark;
return MultiProvider( return MultiProvider(
providers: [ providers: [
BlocProvider<AppSettingsCubit>(
create: (BuildContext context) => AppSettingsCubit(
isDarkModeOn: platformBrightness == Brightness.dark),
),
BlocProvider<ServicesCubit>( BlocProvider<ServicesCubit>(
create: (BuildContext context) => ServicesCubit(), create: (BuildContext context) => ServicesCubit(),
), ),

View File

@ -41,7 +41,8 @@ class BrandColors {
static const dividerColor = gray5; static const dividerColor = gray5;
static const warning = red; static const warning = red;
static get navBackground => white.withOpacity(0.8); static get navBackgroundLight => white.withOpacity(0.8);
static get navBackgroundDark => black.withOpacity(0.8);
static const List<Color> uninitializedGradientColors = [ static const List<Color> uninitializedGradientColors = [
Color(0xFF555555), Color(0xFF555555),

View File

@ -4,7 +4,7 @@ import 'package:selfprivacy/config/text_themes.dart';
import 'brand_colors.dart'; import 'brand_colors.dart';
final theme = ThemeData( final ligtTheme = ThemeData(
primaryColor: BrandColors.primary, primaryColor: BrandColors.primary,
brightness: Brightness.light, brightness: Brightness.light,
scaffoldBackgroundColor: BrandColors.scaffoldBackground, scaffoldBackgroundColor: BrandColors.scaffoldBackground,
@ -24,13 +24,43 @@ final theme = ThemeData(
TextTheme( TextTheme(
headline1: headline1Style, headline1: headline1Style,
headline2: headline2Style, headline2: headline2Style,
caption: captionStyle, caption: headline4Style,
bodyText1: body1Style, bodyText1: body1Style,
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
), ),
), ),
); );
var darkTheme = ligtTheme.copyWith(
brightness: Brightness.dark,
scaffoldBackgroundColor: Color(0xFF202120),
iconTheme: IconThemeData(color: BrandColors.gray3),
cardColor: BrandColors.gray1,
textTheme: GoogleFonts.interTextTheme(
TextTheme(
headline1: headline1Style.copyWith(color: BrandColors.white),
headline2: headline2Style.copyWith(color: BrandColors.white),
caption: headline4Style.copyWith(color: BrandColors.white),
bodyText1: body1Style.copyWith(color: BrandColors.white),
subtitle1: TextStyle(fontSize: 15, height: 1.6), // text input style
),
),
inputDecorationTheme: InputDecorationTheme(
labelStyle: TextStyle(color: BrandColors.white),
hintStyle: TextStyle(color: BrandColors.white),
border: OutlineInputBorder(
borderSide: BorderSide(
color: BrandColors.white,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: BrandColors.white,
),
),
),
);
final brandPagePadding1 = EdgeInsets.symmetric(horizontal: 15, vertical: 30); final brandPagePadding1 = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
final brandPagePadding2 = EdgeInsets.symmetric(horizontal: 15); final brandPagePadding2 = EdgeInsets.symmetric(horizontal: 15);

View File

@ -29,7 +29,7 @@ final headline3Style = GoogleFonts.inter(
color: BrandColors.headlineColor, color: BrandColors.headlineColor,
); );
final captionStyle = GoogleFonts.inter( final headline4Style = GoogleFonts.inter(
fontSize: 18, fontSize: 18,
fontWeight: NamedFontWeight.medium, fontWeight: NamedFontWeight.medium,
color: BrandColors.headlineColor, color: BrandColors.headlineColor,

View File

@ -0,0 +1,18 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
export 'package:provider/provider.dart';
part 'app_settings_state.dart';
class AppSettingsCubit extends Cubit<AppSettingsState> {
AppSettingsCubit({
bool isDarkModeOn,
}) : super(
AppSettingsState(isDarkModeOn: isDarkModeOn),
);
void update({@required bool isDarkModeOn}) {
emit(AppSettingsState(isDarkModeOn: isDarkModeOn));
}
}

View File

@ -0,0 +1,12 @@
part of 'app_settings_cubit.dart';
class AppSettingsState extends Equatable {
const AppSettingsState({
@required this.isDarkModeOn,
});
final bool isDarkModeOn;
@override
List<Object> get props => [isDarkModeOn];
}

View File

@ -7,6 +7,7 @@ import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'config/bloc_config.dart'; import 'config/bloc_config.dart';
import 'config/brand_theme.dart'; import 'config/brand_theme.dart';
import 'config/localization.dart'; import 'config/localization.dart';
import 'logic/cubit/app_settings/app_settings_cubit.dart';
void main() { void main() {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -20,21 +21,22 @@ void main() {
); );
} }
var _showOnbording = false; var _showOnbording = true;
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changnig appbar color value: SystemUiOverlayStyle.light, // Manually changnig appbar color
child: MaterialApp( child: MaterialApp(
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'SelfPrivacy', title: 'SelfPrivacy',
theme: theme, theme: context.watch<AppSettingsCubit>().state.isDarkModeOn
? darkTheme
: ligtTheme,
home: _showOnbording ? OnboardingPage() : RootPage(), home: _showOnbording ? OnboardingPage() : RootPage(),
), ),
); );

View File

@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
enum BrandButtonTypes { rised, text, iconText } enum BrandButtonTypes { rised, text, iconText }
@ -182,9 +182,7 @@ class _IconTextButton extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( BrandText.body1(title),
title,
).body1,
Padding( Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: icon, child: icon,

View File

@ -12,7 +12,9 @@ class BrandCard extends StatelessWidget {
return Container( return Container(
margin: EdgeInsets.only(bottom: 30), margin: EdgeInsets.only(bottom: 30),
decoration: BoxDecoration( decoration: BoxDecoration(
color: BrandColors.white, color: Theme.of(context).brightness == Brightness.dark
? BrandColors.black
: BrandColors.white,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
).ev8, ).ev8,
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class BrandHeader extends StatelessWidget { class BrandHeader extends StatelessWidget {
const BrandHeader({ const BrandHeader({
@ -30,7 +30,7 @@ class BrandHeader extends StatelessWidget {
), ),
SizedBox(width: 10), SizedBox(width: 10),
], ],
Text(title).caption, BrandText.h4(title),
], ],
), ),
), ),

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
class BrandModalSheet extends StatelessWidget { class BrandModalSheet extends StatelessWidget {
const BrandModalSheet({ const BrandModalSheet({
@ -39,7 +38,7 @@ class BrandModalSheet extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius:
BorderRadius.vertical(top: Radius.circular(20)), BorderRadius.vertical(top: Radius.circular(20)),
color: BrandColors.white, color: Theme.of(context).scaffoldBackgroundColor,
), ),
width: double.infinity, width: double.infinity,
child: child, child: child,

View File

@ -4,15 +4,15 @@ import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
final _kBottomTabBarHeight = 51; final _kBottomTabBarHeight = 51;
class BottomTabBar extends StatefulWidget { class BrandTabBar extends StatefulWidget {
BottomTabBar({Key key, this.controller}) : super(key: key); BrandTabBar({Key key, this.controller}) : super(key: key);
final TabController controller; final TabController controller;
@override @override
_BottomTabBarState createState() => _BottomTabBarState(); _BrandTabBarState createState() => _BrandTabBarState();
} }
class _BottomTabBarState extends State<BottomTabBar> { class _BrandTabBarState extends State<BrandTabBar> {
int currentIndex; int currentIndex;
@override @override
void initState() { void initState() {
@ -30,11 +30,14 @@ class _BottomTabBarState extends State<BottomTabBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final paddingBottom = MediaQuery.of(context).padding.bottom; final paddingBottom = MediaQuery.of(context).padding.bottom;
return SizedBox( return SizedBox(
height: paddingBottom + _kBottomTabBarHeight, height: paddingBottom + _kBottomTabBarHeight,
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 16), padding: EdgeInsets.symmetric(horizontal: 16),
color: BrandColors.navBackground, color: Theme.of(context).brightness == Brightness.dark
? BrandColors.navBackgroundDark
: BrandColors.navBackgroundLight,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -50,8 +53,10 @@ class _BottomTabBarState extends State<BottomTabBar> {
} }
_getIconButton(String label, IconData iconData, int index) { _getIconButton(String label, IconData iconData, int index) {
var color = var acitivColor = Theme.of(context).brightness == Brightness.dark
currentIndex == index ? BrandColors.black : BrandColors.inactive; ? BrandColors.white
: BrandColors.black;
var color = currentIndex == index ? acitivColor : BrandColors.inactive;
return InkWell( return InkWell(
onTap: () => widget.controller.animateTo(index), onTap: () => widget.controller.animateTo(index),
child: Padding( child: Padding(

View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/text_themes.dart';
enum TextType {
h1, // right now only at onboarding and opened providers
h2, // cards titles
h3, // titles in about page
h4, // caption
body1, // normal
body2, // with opacity
small
}
class BrandText extends StatelessWidget {
const BrandText(
this.text, {
Key key,
this.style,
@required this.type,
this.overflow,
this.softWrap,
}) : super(key: key);
final String text;
final TextStyle style;
final TextType type;
final TextOverflow overflow;
final bool softWrap;
factory BrandText.h1(
String text, {
TextStyle style,
TextOverflow overflow,
bool softWrap,
}) =>
BrandText(
text,
type: TextType.h1,
style: style,
);
factory BrandText.h2(String text, {TextStyle style}) => BrandText(
text,
type: TextType.h2,
style: style,
);
factory BrandText.h3(String text, {TextStyle style}) => BrandText(
text,
type: TextType.h3,
style: style,
);
factory BrandText.h4(String text, {TextStyle style}) => BrandText(
text,
type: TextType.h4,
style: style,
);
factory BrandText.body1(String text, {TextStyle style}) => BrandText(
text,
type: TextType.body1,
style: style,
);
factory BrandText.body2(String text, {TextStyle style}) => BrandText(
text,
type: TextType.body2,
style: style,
);
factory BrandText.small(String text, {TextStyle style}) => BrandText(
text,
type: TextType.small,
style: style,
);
@override
Text build(BuildContext context) {
TextStyle style;
var isDark = Theme.of(context).brightness == Brightness.dark;
switch (type) {
case TextType.h1:
style = isDark
? headline1Style.copyWith(color: Colors.white)
: headline1Style;
break;
case TextType.h2:
style = isDark
? headline2Style.copyWith(color: Colors.white)
: headline2Style;
break;
case TextType.h3:
style = isDark
? headline3Style.copyWith(color: Colors.white)
: headline3Style;
break;
case TextType.h4:
style = isDark
? headline4Style.copyWith(color: Colors.white)
: headline4Style;
break;
case TextType.body1:
style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style;
break;
case TextType.body2:
style = isDark
? body2Style.copyWith(color: Colors.white.withOpacity(0.6))
: body2Style;
break;
case TextType.small:
style = isDark ? smallStyle.copyWith(color: Colors.white) : smallStyle;
break;
}
if (this.style != null) {
style = style.merge(this.style);
}
return Text(
text,
style: style,
overflow: overflow,
softWrap: softWrap,
);
}
}

View File

@ -6,10 +6,12 @@ class SwitcherBlock extends StatelessWidget {
Key key, Key key,
@required this.child, @required this.child,
@required this.isActive, @required this.isActive,
@required this.onChange,
}) : super(key: key); }) : super(key: key);
final Widget child; final Widget child;
final bool isActive; final bool isActive;
final ValueChanged<bool> onChange;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -28,7 +30,7 @@ class SwitcherBlock extends StatelessWidget {
Switch( Switch(
activeColor: BrandColors.green1, activeColor: BrandColors.green1,
activeTrackColor: BrandColors.green2, activeTrackColor: BrandColors.green2,
onChanged: (v) {}, onChanged: onChange,
value: isActive, value: isActive,
), ),
], ],

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class AboutPage extends StatelessWidget { class AboutPage extends StatelessWidget {
const AboutPage({Key key}) : super(key: key); const AboutPage({Key key}) : super(key: key);
@ -20,28 +20,28 @@ class AboutPage extends StatelessWidget {
children: [ children: [
BrandDivider(), BrandDivider(),
SizedBox(height: 20), SizedBox(height: 20),
Text('О проекте').h3, BrandText.h3('О проекте'),
SizedBox(height: 10), SizedBox(height: 10),
Text('Всё больше организаций хотят владеть нашими данными').body1, BrandText.body1(
'Всё больше организаций хотят владеть нашими данными'),
SizedBox(height: 10), SizedBox(height: 10),
Text('А мы сами хотим распоряжаться своими данными на своем сервере.') BrandText.body1(
.body1, 'А мы сами хотим распоряжаться своими данными на своем сервере.'),
SizedBox(height: 20), SizedBox(height: 20),
BrandDivider(), BrandDivider(),
SizedBox(height: 10), SizedBox(height: 10),
Text('Миссия проекта').h3, BrandText.h3('Миссия проекта'),
SizedBox(height: 10), SizedBox(height: 10),
Text('Цифровая независимость и приватность доступная каждому'), BrandText.body1(
'Цифровая независимость и приватность доступная каждому'),
SizedBox(height: 20), SizedBox(height: 20),
BrandDivider(), BrandDivider(),
SizedBox(height: 10), SizedBox(height: 10),
Text('Цель').h3, BrandText.h3('Цель'),
SizedBox(height: 10), SizedBox(height: 10),
Text( BrandText.body1(
'Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких'), 'Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких'),
SizedBox(height: 10), SizedBox(height: 10),
Text(
'Развивать программу, которая позволит каждому создавать приватные сервисы для себя и своих близких'),
], ],
), ),
), ),

View File

@ -1,37 +1,68 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
class AppSettingsPage extends StatelessWidget { class AppSettingsPage extends StatefulWidget {
const AppSettingsPage({Key key}) : super(key: key); const AppSettingsPage({Key key}) : super(key: key);
@override
_AppSettingsPageState createState() => _AppSettingsPageState();
}
class _AppSettingsPageState extends State<AppSettingsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var appSettings = context.watch<AppSettingsCubit>();
var isDarkModeOn = appSettings.state.isDarkModeOn;
return SafeArea( return SafeArea(
child: Scaffold( child: Builder(builder: (context) {
appBar: PreferredSize( return Scaffold(
child: appBar: PreferredSize(
BrandHeader(title: 'Настройки приложения', hasBackButton: true), child:
preferredSize: Size.fromHeight(52), BrandHeader(title: 'Настройки приложения', hasBackButton: true),
), preferredSize: Size.fromHeight(52),
body: ListView( ),
padding: brandPagePadding2, body: ListView(
children: [ padding: brandPagePadding2,
BrandDivider(), children: [
SwitcherBlock( BrandDivider(),
child: _TextColumn( Container(
title: 'Dark Theme', padding: EdgeInsets.only(top: 20, bottom: 5),
value: 'Change your the app theme', decoration: BoxDecoration(
), border: Border(
isActive: true, bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
), )),
], child: Row(
), crossAxisAlignment: CrossAxisAlignment.start,
), mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: _TextColumn(
title: 'Dark Theme',
value: 'Change your the app theme',
),
),
SizedBox(width: 5),
Switch(
activeColor: BrandColors.green1,
activeTrackColor: BrandColors.green2,
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (value) =>
appSettings.update(isDarkModeOn: !isDarkModeOn),
),
],
),
)
],
),
);
}),
); );
} }
} }
@ -52,21 +83,21 @@ class _TextColumn extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(title).body1.copyWith( BrandText.body1(
style: TextStyle(color: hasWarning ? BrandColors.warning : null)), title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
SizedBox(height: 5), SizedBox(height: 5),
Text(value) BrandText.body1(
.body1 title,
.copyWith( style: TextStyle(color: hasWarning ? BrandColors.warning : null),
style: TextStyle( ),
fontSize: 13, BrandText.body1(value,
height: 1.53, style: TextStyle(
color: BrandColors.gray1, fontSize: 13,
), height: 1.53,
) color: BrandColors.gray1,
.copyWith( ).merge(TextStyle(color: hasWarning ? BrandColors.warning : null))),
style:
TextStyle(color: hasWarning ? BrandColors.warning : null)),
], ],
); );
} }

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:package_info/package_info.dart'; import 'package:package_info/package_info.dart';
class InfoPage extends StatelessWidget { class InfoPage extends StatelessWidget {
@ -24,9 +24,8 @@ class InfoPage extends StatelessWidget {
FutureBuilder( FutureBuilder(
future: _version(), future: _version(),
builder: (context, snapshot) { builder: (context, snapshot) {
return Text( return BrandText.body1(
'Тут любая служебная информация, v.${snapshot.data}') 'Тут любая служебная информация, v.${snapshot.data}');
.body1;
}), }),
], ],
), ),

View File

@ -4,7 +4,7 @@ import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'about/about.dart'; import 'about/about.dart';
@ -80,11 +80,14 @@ class _NavItem extends StatelessWidget {
), ),
child: Row( child: Row(
children: [ children: [
Text(title).body1, BrandText.body1(title),
Spacer(), Spacer(),
SizedBox( SizedBox(
width: 56, width: 56,
child: Icon(iconData, size: 20), child: Icon(
iconData,
size: 20,
),
), ),
], ],
), ),

View File

@ -1,340 +1,53 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.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_text/brand_text.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/ui/components/dots_indicator/dots_indicator.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
class OnboardingPage extends StatefulWidget { class OnboardingPage extends StatelessWidget {
const OnboardingPage({Key key}) : super(key: key); const OnboardingPage({Key key}) : super(key: key);
@override
_OnboardingPageState createState() => _OnboardingPageState();
}
class _OnboardingPageState extends State<OnboardingPage> {
PageController controller;
var currentPage = 0;
@override
void initState() {
controller = PageController(
initialPage: 0,
)..addListener(() {
if (currentPage != controller.page.toInt()) {
setState(() {
currentPage = controller.page.toInt();
});
}
});
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {});
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var steps = getSteps();
return SafeArea( return SafeArea(
child: Scaffold( child: Scaffold(
body: ListView( body: Padding(
shrinkWrap: true, padding: const EdgeInsets.symmetric(
children: [ horizontal: 15,
Padding( vertical: 45,
padding: brandPagePadding1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Начало').caption,
Text('SelfPrivacy').h1,
SizedBox(
height: 10,
),
RichText(
text: TextSpan(
children: [
TextSpan(
text:
'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
style: body2Style,
),
BrandSpanButton.link(
text: 'selfprivacy.org/start',
urlString: 'https://selfprivacy.org/start',
),
],
),
),
],
),
),
Container(
height: 480,
child: PageView.builder(
physics: NeverScrollableScrollPhysics(),
allowImplicitScrolling: false,
controller: controller,
itemBuilder: (_, index) {
return Padding(
padding: brandPagePadding2,
child: steps[index],
);
},
itemCount: 4,
),
),
DotsIndicator(
activeIndex: currentPage,
count: steps.length,
),
SizedBox(height: 50),
],
),
),
);
}
List<Widget> getSteps() => <Widget>[
BrandCard(
child: Column(
mainAxisSize: MainAxisSize.min,
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(
onPressed: _nextPage,
hintText: 'Hetzner API Token',
length: 2,
),
SizedBox(height: 20),
Spacer(),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
),
],
), ),
),
BrandCard(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset('assets/images/logos/namecheap.png'), Expanded(
SizedBox(height: 10), child: Align(
Text('2. Настройте домен ').h2, alignment: Alignment.centerLeft,
SizedBox(height: 10), child: Column(
RichText( mainAxisAlignment: MainAxisAlignment.center,
text: TextSpan( crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextSpan( BrandText.h1(
text: 'Зарегистрируйте домен в ', 'Онбординг',
style: body2Style, ),
), SizedBox(height: 20),
BrandSpanButton.link( BrandText.body2(
text: 'NameCheap', 'Тут рассказ на 1-2 слайда о том, что делает это приложение, какие твои проблемы решает и как (в общем чего ожидать от сервиса).',
urlString: 'https://www.namecheap.com', ),
), ],
TextSpan( ),
text:
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
style: body2Style,
),
],
), ),
), ),
_MockForm( BrandButton.rised(
onPressed: _nextPage,
hintText: 'Домен, например, selfprivacy.org',
submitButtonText: 'Проверить DNS',
length: 2,
),
Spacer(),
BrandButton.text(
onPressed: () {},
title: 'Как настроить DNS CloudFlare',
),
],
),
),
BrandCard(
child: Column(
mainAxisSize: MainAxisSize.min,
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(
onPressed: _nextPage,
hintText: 'CloudFlare API Token',
length: 2,
),
Spacer(),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
),
),
BrandCard(
child: Column(
mainAxisSize: MainAxisSize.min,
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(
onPressed: () { onPressed: () {
Navigator.of(context) Navigator.of(context)
.pushReplacement(materialRoute(RootPage())); .pushReplacement(materialRoute(RootPage()));
}, },
hintText: 'Amazon AWS Access Key', title: 'Приступим!',
length: 2, )
),
Spacer(),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
], ],
), ),
), ),
];
void _showModal(BuildContext context, Widget widget) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return widget;
},
);
}
void _nextPage() => controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
class _HowHetzner extends StatelessWidget {
const _HowHetzner({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BrandModalSheet(
child: Padding(
padding: brandPagePadding2,
child: Column(
children: [
SizedBox(height: 40),
Text('Как получить Hetzner API Token').h2,
SizedBox(height: 20),
RichText(
text: TextSpan(
children: [
TextSpan(
text: '1 Переходим по ссылке ',
style: body1Style,
),
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: body1Style,
),
],
),
),
],
),
), ),
); );
} }
} }
class _MockForm extends StatefulWidget {
const _MockForm({
Key key,
@required this.hintText,
this.submitButtonText = 'Подключить',
@required this.onPressed,
@required this.length,
}) : super(key: key);
final String hintText;
final String submitButtonText;
final int length;
final VoidCallback onPressed;
@override
__MockFormState createState() => __MockFormState();
}
class __MockFormState extends State<_MockForm> {
String text = '';
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(height: 20),
TextField(
onChanged: (value) => {
setState(() {
text = value;
})
},
decoration: InputDecoration(hintText: widget.hintText),
),
SizedBox(height: 20),
BrandButton.rised(
onPressed:
text.length == widget.length ? widget.onPressed ?? () {} : null,
title: widget.submitButtonText,
),
],
);
}
}

View File

@ -5,7 +5,7 @@ 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_card/brand_card.dart';
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.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/ui/components/brand_span_button/brand_span_button.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class OnboardingPage extends StatelessWidget { class OnboardingPage extends StatelessWidget {
const OnboardingPage({Key key}) : super(key: key); const OnboardingPage({Key key}) : super(key: key);
@ -16,8 +16,8 @@ class OnboardingPage extends StatelessWidget {
body: ListView( body: ListView(
padding: brandPagePadding1, padding: brandPagePadding1,
children: [ children: [
Text('Начало').caption, BrandText.h4('Начало'),
Text('SelfPrivacy').h1, BrandText.h1('SelfPrivacy'),
SizedBox( SizedBox(
height: 10, height: 10,
), ),
@ -43,10 +43,10 @@ class OnboardingPage extends StatelessWidget {
children: [ children: [
Image.asset('assets/images/logos/hetzner.png'), Image.asset('assets/images/logos/hetzner.png'),
SizedBox(height: 10), SizedBox(height: 10),
Text('1. Подключите сервер Hetzner').h2, BrandText.h2('1. Подключите сервер Hetzner'),
SizedBox(height: 10), SizedBox(height: 10),
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы') BrandText.body2(
.body2, 'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
_MockForm( _MockForm(
hintText: 'Hetzner API Token', hintText: 'Hetzner API Token',
), ),
@ -64,7 +64,7 @@ class OnboardingPage extends StatelessWidget {
children: [ children: [
Image.asset('assets/images/logos/namecheap.png'), Image.asset('assets/images/logos/namecheap.png'),
SizedBox(height: 10), SizedBox(height: 10),
Text('2. Настройте домен ').h2, BrandText.h2('2. Настройте домен'),
SizedBox(height: 10), SizedBox(height: 10),
RichText( RichText(
text: TextSpan( text: TextSpan(
@ -103,9 +103,9 @@ class OnboardingPage extends StatelessWidget {
children: [ children: [
Image.asset('assets/images/logos/cloudflare.png'), Image.asset('assets/images/logos/cloudflare.png'),
SizedBox(height: 10), SizedBox(height: 10),
Text('3. Подключите CloudFlare DNS').h2, BrandText.h2('3. Подключите CloudFlare DNS'),
SizedBox(height: 10), SizedBox(height: 10),
Text('Для управления DNS вашего домена').body2, BrandText.body2('Для управления DNS вашего домена'),
_MockForm( _MockForm(
hintText: 'CloudFlare API Token', hintText: 'CloudFlare API Token',
), ),
@ -123,10 +123,10 @@ class OnboardingPage extends StatelessWidget {
children: [ children: [
Image.asset('assets/images/logos/aws.png'), Image.asset('assets/images/logos/aws.png'),
SizedBox(height: 10), SizedBox(height: 10),
Text('4. Подключите Amazon AWS для бекапа').h2, BrandText.h2('4. Подключите Amazon AWS для бекапа'),
SizedBox(height: 10), SizedBox(height: 10),
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде') BrandText.body2(
.body2, 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'),
_MockForm( _MockForm(
hintText: 'Amazon AWS Access Key', hintText: 'Amazon AWS Access Key',
), ),
@ -163,25 +163,27 @@ class _HowHetzner extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BrandModalSheet( return BrandModalSheet(
child: Column( child: Padding(
children: [ padding: brandPagePadding2,
SizedBox(height: 40), child: Column(
Text('Как получить Hetzner API Token').h2, children: [
SizedBox(height: 20), SizedBox(height: 40),
RichText( BrandText.h2('Как получить Hetzner API Token'),
text: TextSpan( SizedBox(height: 20),
children: [ RichText(
TextSpan( text: TextSpan(
text: '1 Переходим по ссылке ', children: [
style: body1Style, TextSpan(
), text: '1 Переходим по ссылке ',
BrandSpanButton.link( style: body1Style,
text: 'hetzner.com/sdfsdfsdfsdf', ),
urlString: 'https://hetzner.com/sdfsdfsdfsdf', BrandSpanButton.link(
), text: 'hetzner.com/sdfsdfsdfsdf',
TextSpan( urlString: 'https://hetzner.com/sdfsdfsdfsdf',
text: ''' ),
TextSpan(
text: '''
2 Заходим в созданный нами проект. Если такового - нет, значит создаём. 2 Заходим в созданный нами проект. Если такового - нет, значит создаём.
3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний Security (с иконкой ключика). 3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний Security (с иконкой ключика).
@ -192,13 +194,14 @@ class _HowHetzner extends StatelessWidget {
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
''', ''',
style: body1Style, style: body1Style,
), ),
], ],
),
), ),
), ],
], ),
), ),
); );
} }

View File

@ -0,0 +1,340 @@
// import 'package:flutter/material.dart';
// import 'package:selfprivacy/config/brand_theme.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/ui/components/brand_text/brand_text.dart';
// import 'package:selfprivacy/ui/components/dots_indicator/dots_indicator.dart';
// import 'package:selfprivacy/ui/pages/rootRoute.dart';
// import 'package:selfprivacy/utils/route_transitions/basic.dart';
// class OnboardingPage extends StatefulWidget {
// const OnboardingPage({Key key}) : super(key: key);
// @override
// _OnboardingPageState createState() => _OnboardingPageState();
// }
// class _OnboardingPageState extends State<OnboardingPage> {
// PageController controller;
// var currentPage = 0;
// @override
// void initState() {
// controller = PageController(
// initialPage: 0,
// )..addListener(() {
// if (currentPage != controller.page.toInt()) {
// setState(() {
// currentPage = controller.page.toInt();
// });
// }
// });
// super.initState();
// WidgetsBinding.instance.addPostFrameCallback((_) {});
// }
// @override
// void dispose() {
// controller.dispose();
// super.dispose();
// }
// @override
// Widget build(BuildContext context) {
// var steps = getSteps();
// return SafeArea(
// child: Scaffold(
// body: ListView(
// shrinkWrap: true,
// children: [
// Padding(
// padding: brandPagePadding1,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// BrandText.h4('Начало'),
// BrandText.h1('SelfPrivacy'),
// SizedBox(
// height: 10,
// ),
// RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text:
// 'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
// style: body2Style,
// ),
// BrandSpanButton.link(
// text: 'selfprivacy.org/start',
// urlString: 'https://selfprivacy.org/start',
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// Container(
// height: 480,
// child: PageView.builder(
// physics: NeverScrollableScrollPhysics(),
// allowImplicitScrolling: false,
// controller: controller,
// itemBuilder: (_, index) {
// return Padding(
// padding: brandPagePadding2,
// child: steps[index],
// );
// },
// itemCount: 4,
// ),
// ),
// DotsIndicator(
// activeIndex: currentPage,
// count: steps.length,
// ),
// SizedBox(height: 50),
// ],
// ),
// ),
// );
// }
// List<Widget> getSteps() => <Widget>[
// BrandCard(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// 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(
// onPressed: _nextPage,
// hintText: 'Hetzner API Token',
// length: 2,
// ),
// SizedBox(height: 20),
// Spacer(),
// BrandButton.text(
// onPressed: () => _showModal(context, _HowHetzner()),
// title: 'Как получить API Token',
// ),
// ],
// ),
// ),
// BrandCard(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// 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: body2Style,
// ),
// BrandSpanButton.link(
// text: 'NameCheap',
// urlString: 'https://www.namecheap.com',
// ),
// TextSpan(
// text:
// ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
// style: body2Style,
// ),
// ],
// ),
// ),
// _MockForm(
// onPressed: _nextPage,
// hintText: 'Домен, например, selfprivacy.org',
// submitButtonText: 'Проверить DNS',
// length: 2,
// ),
// Spacer(),
// BrandButton.text(
// onPressed: () {},
// title: 'Как настроить DNS CloudFlare',
// ),
// ],
// ),
// ),
// BrandCard(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// 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(
// onPressed: _nextPage,
// hintText: 'CloudFlare API Token',
// length: 2,
// ),
// Spacer(),
// BrandButton.text(
// onPressed: () {},
// title: 'Как получить API Token',
// ),
// ],
// ),
// ),
// BrandCard(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// 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(
// onPressed: () {
// Navigator.of(context)
// .pushReplacement(materialRoute(RootPage()));
// },
// hintText: 'Amazon AWS Access Key',
// length: 2,
// ),
// Spacer(),
// BrandButton.text(
// onPressed: () {},
// title: 'Как получить API Token',
// ),
// ],
// ),
// ),
// ];
// void _showModal(BuildContext context, Widget widget) {
// showModalBottomSheet<void>(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (BuildContext context) {
// return widget;
// },
// );
// }
// void _nextPage() => controller.nextPage(
// duration: Duration(milliseconds: 300),
// curve: Curves.easeIn,
// );
// }
// class _HowHetzner extends StatelessWidget {
// const _HowHetzner({
// Key key,
// }) : super(key: key);
// @override
// Widget build(BuildContext context) {
// return BrandModalSheet(
// child: Padding(
// padding: brandPagePadding2,
// child: Column(
// children: [
// SizedBox(height: 40),
// Text('Как получить Hetzner API Token').h2,
// SizedBox(height: 20),
// RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text: '1 Переходим по ссылке ',
// style: body1Style,
// ),
// 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: body1Style,
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// );
// }
// }
// class _MockForm extends StatefulWidget {
// const _MockForm({
// Key key,
// @required this.hintText,
// this.submitButtonText = 'Подключить',
// @required this.onPressed,
// @required this.length,
// }) : super(key: key);
// final String hintText;
// final String submitButtonText;
// final int length;
// final VoidCallback onPressed;
// @override
// __MockFormState createState() => __MockFormState();
// }
// class __MockFormState extends State<_MockForm> {
// String text = '';
// @override
// Widget build(BuildContext context) {
// return Column(
// children: [
// SizedBox(height: 20),
// TextField(
// onChanged: (value) => {
// setState(() {
// text = value;
// })
// },
// decoration: InputDecoration(hintText: widget.hintText),
// ),
// SizedBox(height: 20),
// BrandButton.rised(
// onPressed:
// text.length == widget.length ? widget.onPressed ?? () {} : null,
// title: widget.submitButtonText,
// ),
// ],
// );
// }
// }

View File

@ -5,9 +5,9 @@ import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; import 'package:selfprivacy/ui/components/brand_card/brand_card.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/pages/providers/settings/setting.dart'; import 'package:selfprivacy/ui/pages/providers/settings/setting.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ProvidersPage extends StatefulWidget { class ProvidersPage extends StatefulWidget {
@ -85,14 +85,14 @@ class _Card extends StatelessWidget {
child: Icon(provider.icon, size: 30, color: Colors.white), child: Icon(provider.icon, size: 30, color: Colors.white),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text(title).h2, BrandText.h2(title),
SizedBox(height: 10), SizedBox(height: 10),
if (message != null) ...[ if (message != null) ...[
Text(message).body2, BrandText.body2(message),
SizedBox(height: 10), SizedBox(height: 10),
], ],
if (provider.state == ServiceStateType.stable) if (provider.state == ServiceStateType.stable)
Text(stableText).body2, BrandText.body2(stableText),
], ],
), ),
), ),
@ -173,9 +173,9 @@ class _ProviderDetails extends StatelessWidget {
child: Icon(provider.icon, size: 40, color: Colors.white), child: Icon(provider.icon, size: 40, color: Colors.white),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text(title).h1, BrandText.h1(title),
SizedBox(height: 10), SizedBox(height: 10),
Text(statusText).body1, BrandText.body1(statusText),
SizedBox( SizedBox(
height: 20, height: 20,
), ),

View File

@ -3,8 +3,8 @@ import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
class SettingsPage extends StatelessWidget { class SettingsPage extends StatelessWidget {
const SettingsPage({Key key}) : super(key: key); const SettingsPage({Key key}) : super(key: key);
@ -22,6 +22,7 @@ class SettingsPage extends StatelessWidget {
children: [ children: [
BrandDivider(), BrandDivider(),
SwitcherBlock( SwitcherBlock(
onChange: (_) {},
child: _TextColumn( child: _TextColumn(
title: 'Allow Auto-upgrade', title: 'Allow Auto-upgrade',
value: 'Wether to allow automatic packages upgrades', value: 'Wether to allow automatic packages upgrades',
@ -29,6 +30,7 @@ class SettingsPage extends StatelessWidget {
isActive: true, isActive: true,
), ),
SwitcherBlock( SwitcherBlock(
onChange: (_) {},
child: _TextColumn( child: _TextColumn(
title: 'Reboot after upgrade', title: 'Reboot after upgrade',
value: 'Reboot without prompt after applying updates', value: 'Reboot without prompt after applying updates',
@ -106,21 +108,19 @@ class _TextColumn extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(title).body1.copyWith( BrandText.body1(
style: TextStyle(color: hasWarning ? BrandColors.warning : null)), title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
SizedBox(height: 5), SizedBox(height: 5),
Text(value) BrandText.body1(
.body1 value,
.copyWith( style: TextStyle(
style: TextStyle( fontSize: 13,
fontSize: 13, height: 1.53,
height: 1.53, color: hasWarning ? BrandColors.warning : BrandColors.gray1,
color: BrandColors.gray1, ),
), ),
)
.copyWith(
style:
TextStyle(color: hasWarning ? BrandColors.warning : null)),
], ],
); );
} }

View File

@ -42,7 +42,7 @@ class _RootPageState extends State<RootPage>
MorePage(), MorePage(),
], ],
), ),
bottomNavigationBar: BottomTabBar( bottomNavigationBar: BrandTabBar(
controller: tabController, controller: tabController,
), ),
), ),

View File

@ -6,8 +6,8 @@ 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_card/brand_card.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
class ServicesPage extends StatefulWidget { class ServicesPage extends StatefulWidget {
ServicesPage({Key key}) : super(key: key); ServicesPage({Key key}) : super(key: key);
@ -33,7 +33,7 @@ class _ServicesPageState extends State<ServicesPage> {
SizedBox(height: 24), SizedBox(height: 24),
...connected.map((service) => _Card(service: service)).toList(), ...connected.map((service) => _Card(service: service)).toList(),
if (uninitialized.isNotEmpty) ...[ if (uninitialized.isNotEmpty) ...[
Text('не подключены').body1, BrandText.body1('не подключены'),
SizedBox(height: 30), SizedBox(height: 30),
], ],
...uninitialized.map((service) => _Card(service: service)).toList() ...uninitialized.map((service) => _Card(service: service)).toList()
@ -91,10 +91,10 @@ class _Card extends StatelessWidget {
child: Icon(iconData, size: 30, color: Colors.white), child: Icon(iconData, size: 30, color: Colors.white),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text(title).h2, BrandText.h2(title),
SizedBox(height: 10), SizedBox(height: 10),
if (service.state == ServiceStateType.uninitialized) ...[ if (service.state == ServiceStateType.uninitialized) ...[
Text(description).body1, BrandText.body1(description),
SizedBox(height: 10), SizedBox(height: 10),
BrandButton.text( BrandButton.text(
title: 'Подключить', title: 'Подключить',
@ -102,7 +102,8 @@ class _Card extends StatelessWidget {
context.read<ServicesCubit>().connect(service); context.read<ServicesCubit>().connect(service);
}) })
], ],
if (service.state == ServiceStateType.stable) Text('Подключен').body1, if (service.state == ServiceStateType.stable)
BrandText.body2('Подключен'),
], ],
), ),
); );

View File

@ -8,7 +8,7 @@ import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/password_generator.dart'; import 'package:selfprivacy/utils/password_generator.dart';
class UsersPage extends StatelessWidget { class UsersPage extends StatelessWidget {
@ -88,7 +88,7 @@ class _User extends StatelessWidget {
), ),
), ),
SizedBox(width: 20), SizedBox(width: 20),
Text(user.login).caption, BrandText.h4(user.login),
], ],
), ),
), ),
@ -267,16 +267,15 @@ class _UserDetails extends StatelessWidget {
), ),
Spacer(), Spacer(),
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: 20, vertical: 20,
horizontal: 15, horizontal: 15,
), ),
child: Text( child: BrandText.h1(
user.login, user.login,
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
).h1, )),
),
], ],
), ),
), ),
@ -286,18 +285,18 @@ class _UserDetails extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Учетная запись').small, BrandText.small('Учетная запись'),
Container( Container(
height: 40, height: 40,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text('${user.login}@example.com').caption, child: BrandText.h4('${user.login}@example.com'),
), ),
SizedBox(height: 14), SizedBox(height: 14),
Text('Пароль').small, BrandText.small('Пароль'),
Container( Container(
height: 40, height: 40,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(user.password).caption, child: BrandText.h4(user.password),
), ),
SizedBox(height: 24), SizedBox(height: 24),
BrandDivider(), BrandDivider(),

View File

@ -1,47 +1,47 @@
library text_extension; // library text_extension;
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; // import 'package:flutter/cupertino.dart';
import 'package:selfprivacy/config/text_themes.dart'; // import 'package:selfprivacy/config/text_themes.dart';
extension TextExtension on Text { // extension TextExtension on Text {
Text get h1 => copyWith(style: headline1Style); // Text get h1 => copyWith(style: headline1Style);
Text get h2 => copyWith(style: headline2Style); // Text get h2 => copyWith(style: headline2Style);
Text get h3 => copyWith(style: headline3Style); // Text get h3 => copyWith(style: headline3Style);
Text get caption => copyWith(style: captionStyle); // Text get caption => copyWith(style: headline4Style);
Text get body1 => copyWith(style: body1Style); // Text get body1 => copyWith(style: body1Style);
Text get body2 => copyWith(style: body2Style); // Text get body2 => copyWith(style: body2Style);
Text get small => copyWith(style: smallStyle); // Text get small => copyWith(style: smallStyle);
Text setKey(Key key) => copyWith(key: key); // Text setKey(Key key) => copyWith(key: key);
Text copyWith( // Text copyWith(
{Key key, // {Key key,
StrutStyle strutStyle, // StrutStyle strutStyle,
TextAlign textAlign, // TextAlign textAlign,
TextDirection textDirection = TextDirection.ltr, // TextDirection textDirection = TextDirection.ltr,
Locale locale, // Locale locale,
bool softWrap, // bool softWrap,
TextOverflow overflow, // TextOverflow overflow,
double textScaleFactor, // double textScaleFactor,
int maxLines, // int maxLines,
String semanticsLabel, // String semanticsLabel,
TextWidthBasis textWidthBasis, // TextWidthBasis textWidthBasis,
TextStyle style}) { // TextStyle style}) {
return Text(data, // return Text(data,
key: key ?? this.key, // key: key ?? this.key,
strutStyle: strutStyle ?? this.strutStyle, // strutStyle: strutStyle ?? this.strutStyle,
textAlign: textAlign ?? this.textAlign, // textAlign: textAlign ?? this.textAlign,
textDirection: textDirection ?? this.textDirection, // textDirection: textDirection ?? this.textDirection,
locale: locale ?? this.locale, // locale: locale ?? this.locale,
softWrap: softWrap ?? this.softWrap, // softWrap: softWrap ?? this.softWrap,
overflow: overflow ?? this.overflow, // overflow: overflow ?? this.overflow,
textScaleFactor: textScaleFactor ?? this.textScaleFactor, // textScaleFactor: textScaleFactor ?? this.textScaleFactor,
maxLines: maxLines ?? this.maxLines, // maxLines: maxLines ?? this.maxLines,
semanticsLabel: semanticsLabel ?? this.semanticsLabel, // semanticsLabel: semanticsLabel ?? this.semanticsLabel,
textWidthBasis: textWidthBasis ?? this.textWidthBasis, // textWidthBasis: textWidthBasis ?? this.textWidthBasis,
style: style != null ? this.style?.merge(style) ?? style : this.style); // style: style != null ? this.style?.merge(style) ?? style : this.style);
} // }
} // }