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/scheduler.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/users/users_cubit.dart';
@ -10,8 +12,15 @@ class BlocAndProviderConfig extends StatelessWidget {
@override
Widget build(BuildContext context) {
var platformBrightness =
SchedulerBinding.instance.window.platformBrightness;
// var platformBrightness = Brightness.dark;
return MultiProvider(
providers: [
BlocProvider<AppSettingsCubit>(
create: (BuildContext context) => AppSettingsCubit(
isDarkModeOn: platformBrightness == Brightness.dark),
),
BlocProvider<ServicesCubit>(
create: (BuildContext context) => ServicesCubit(),
),

View File

@ -41,7 +41,8 @@ class BrandColors {
static const dividerColor = gray5;
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 = [
Color(0xFF555555),

View File

@ -4,7 +4,7 @@ import 'package:selfprivacy/config/text_themes.dart';
import 'brand_colors.dart';
final theme = ThemeData(
final ligtTheme = ThemeData(
primaryColor: BrandColors.primary,
brightness: Brightness.light,
scaffoldBackgroundColor: BrandColors.scaffoldBackground,
@ -24,13 +24,43 @@ final theme = ThemeData(
TextTheme(
headline1: headline1Style,
headline2: headline2Style,
caption: captionStyle,
caption: headline4Style,
bodyText1: body1Style,
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 brandPagePadding2 = EdgeInsets.symmetric(horizontal: 15);

View File

@ -29,7 +29,7 @@ final headline3Style = GoogleFonts.inter(
color: BrandColors.headlineColor,
);
final captionStyle = GoogleFonts.inter(
final headline4Style = GoogleFonts.inter(
fontSize: 18,
fontWeight: NamedFontWeight.medium,
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/brand_theme.dart';
import 'config/localization.dart';
import 'logic/cubit/app_settings/app_settings_cubit.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
@ -20,21 +21,22 @@ void main() {
);
}
var _showOnbording = false;
var _showOnbording = true;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changnig appbar color
child: MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
title: 'SelfPrivacy',
theme: theme,
theme: context.watch<AppSettingsCubit>().state.isDarkModeOn
? darkTheme
: ligtTheme,
home: _showOnbording ? OnboardingPage() : RootPage(),
),
);

View File

@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.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 }
@ -182,9 +182,7 @@ class _IconTextButton extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
).body1,
BrandText.body1(title),
Padding(
padding: const EdgeInsets.all(12.0),
child: icon,

View File

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

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.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 {
const BrandHeader({
@ -30,7 +30,7 @@ class BrandHeader extends StatelessWidget {
),
SizedBox(width: 10),
],
Text(title).caption,
BrandText.h4(title),
],
),
),

View File

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

View File

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

View File

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

View File

@ -1,18 +1,28 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.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_header/brand_header.dart';
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class AppSettingsPage extends StatelessWidget {
class AppSettingsPage extends StatefulWidget {
const AppSettingsPage({Key key}) : super(key: key);
@override
_AppSettingsPageState createState() => _AppSettingsPageState();
}
class _AppSettingsPageState extends State<AppSettingsPage> {
@override
Widget build(BuildContext context) {
var appSettings = context.watch<AppSettingsCubit>();
var isDarkModeOn = appSettings.state.isDarkModeOn;
return SafeArea(
child: Scaffold(
child: Builder(builder: (context) {
return Scaffold(
appBar: PreferredSize(
child:
BrandHeader(title: 'Настройки приложения', hasBackButton: true),
@ -22,17 +32,38 @@ class AppSettingsPage extends StatelessWidget {
padding: brandPagePadding2,
children: [
BrandDivider(),
SwitcherBlock(
Container(
padding: EdgeInsets.only(top: 20, bottom: 5),
decoration: BoxDecoration(
border: Border(
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',
),
isActive: true,
),
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(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title).body1.copyWith(
style: TextStyle(color: hasWarning ? BrandColors.warning : null)),
BrandText.body1(
title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
SizedBox(height: 5),
Text(value)
.body1
.copyWith(
BrandText.body1(
title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
BrandText.body1(value,
style: TextStyle(
fontSize: 13,
height: 1.53,
color: BrandColors.gray1,
),
)
.copyWith(
style:
TextStyle(color: hasWarning ? BrandColors.warning : null)),
).merge(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/ui/components/brand_divider/brand_divider.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';
class InfoPage extends StatelessWidget {
@ -24,9 +24,8 @@ class InfoPage extends StatelessWidget {
FutureBuilder(
future: _version(),
builder: (context, snapshot) {
return Text(
'Тут любая служебная информация, v.${snapshot.data}')
.body1;
return BrandText.body1(
'Тут любая служебная информация, v.${snapshot.data}');
}),
],
),

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_header/brand_header.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 'about/about.dart';
@ -80,11 +80,14 @@ class _NavItem extends StatelessWidget {
),
child: Row(
children: [
Text(title).body1,
BrandText.body1(title),
Spacer(),
SizedBox(
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: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/dots_indicator/dots_indicator.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class OnboardingPage extends StatefulWidget {
class OnboardingPage extends StatelessWidget {
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,
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 45,
),
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(
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
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,
BrandText.h1(
'Онбординг',
),
SizedBox(height: 20),
Spacer(),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
BrandText.body2(
'Тут рассказ на 1-2 слайда о том, что делает это приложение, какие твои проблемы решает и как (в общем чего ожидать от сервиса).',
),
],
),
),
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(
BrandButton.rised(
onPressed: () {
Navigator.of(context)
.pushReplacement(materialRoute(RootPage()));
},
hintText: 'Amazon AWS Access Key',
length: 2,
),
Spacer(),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
title: 'Приступим!',
)
],
),
),
];
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_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';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
class OnboardingPage extends StatelessWidget {
const OnboardingPage({Key key}) : super(key: key);
@ -16,8 +16,8 @@ class OnboardingPage extends StatelessWidget {
body: ListView(
padding: brandPagePadding1,
children: [
Text('Начало').caption,
Text('SelfPrivacy').h1,
BrandText.h4('Начало'),
BrandText.h1('SelfPrivacy'),
SizedBox(
height: 10,
),
@ -43,10 +43,10 @@ class OnboardingPage extends StatelessWidget {
children: [
Image.asset('assets/images/logos/hetzner.png'),
SizedBox(height: 10),
Text('1. Подключите сервер Hetzner').h2,
BrandText.h2('1. Подключите сервер Hetzner'),
SizedBox(height: 10),
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы')
.body2,
BrandText.body2(
'Здесь будут жить наши данные и SelfPrivacy-сервисы'),
_MockForm(
hintText: 'Hetzner API Token',
),
@ -64,7 +64,7 @@ class OnboardingPage extends StatelessWidget {
children: [
Image.asset('assets/images/logos/namecheap.png'),
SizedBox(height: 10),
Text('2. Настройте домен ').h2,
BrandText.h2('2. Настройте домен'),
SizedBox(height: 10),
RichText(
text: TextSpan(
@ -103,9 +103,9 @@ class OnboardingPage extends StatelessWidget {
children: [
Image.asset('assets/images/logos/cloudflare.png'),
SizedBox(height: 10),
Text('3. Подключите CloudFlare DNS').h2,
BrandText.h2('3. Подключите CloudFlare DNS'),
SizedBox(height: 10),
Text('Для управления DNS вашего домена').body2,
BrandText.body2('Для управления DNS вашего домена'),
_MockForm(
hintText: 'CloudFlare API Token',
),
@ -123,10 +123,10 @@ class OnboardingPage extends StatelessWidget {
children: [
Image.asset('assets/images/logos/aws.png'),
SizedBox(height: 10),
Text('4. Подключите Amazon AWS для бекапа').h2,
BrandText.h2('4. Подключите Amazon AWS для бекапа'),
SizedBox(height: 10),
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
.body2,
BrandText.body2(
'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'),
_MockForm(
hintText: 'Amazon AWS Access Key',
),
@ -163,10 +163,12 @@ class _HowHetzner extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BrandModalSheet(
child: Padding(
padding: brandPagePadding2,
child: Column(
children: [
SizedBox(height: 40),
Text('Как получить Hetzner API Token').h2,
BrandText.h2('Как получить Hetzner API Token'),
SizedBox(height: 20),
RichText(
text: TextSpan(
@ -200,6 +202,7 @@ class _HowHetzner extends StatelessWidget {
),
],
),
),
);
}
}

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_header/brand_header.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/pages/providers/settings/setting.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ProvidersPage extends StatefulWidget {
@ -85,14 +85,14 @@ class _Card extends StatelessWidget {
child: Icon(provider.icon, size: 30, color: Colors.white),
),
SizedBox(height: 10),
Text(title).h2,
BrandText.h2(title),
SizedBox(height: 10),
if (message != null) ...[
Text(message).body2,
BrandText.body2(message),
SizedBox(height: 10),
],
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),
),
SizedBox(height: 10),
Text(title).h1,
BrandText.h1(title),
SizedBox(height: 10),
Text(statusText).body1,
BrandText.body1(statusText),
SizedBox(
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/ui/components/brand_divider/brand_divider.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/utils/extensions/text_extension.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({Key key}) : super(key: key);
@ -22,6 +22,7 @@ class SettingsPage extends StatelessWidget {
children: [
BrandDivider(),
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Allow Auto-upgrade',
value: 'Wether to allow automatic packages upgrades',
@ -29,6 +30,7 @@ class SettingsPage extends StatelessWidget {
isActive: true,
),
SwitcherBlock(
onChange: (_) {},
child: _TextColumn(
title: 'Reboot after upgrade',
value: 'Reboot without prompt after applying updates',
@ -106,21 +108,19 @@ class _TextColumn extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title).body1.copyWith(
style: TextStyle(color: hasWarning ? BrandColors.warning : null)),
BrandText.body1(
title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
SizedBox(height: 5),
Text(value)
.body1
.copyWith(
BrandText.body1(
value,
style: TextStyle(
fontSize: 13,
height: 1.53,
color: BrandColors.gray1,
color: hasWarning ? BrandColors.warning : BrandColors.gray1,
),
),
)
.copyWith(
style:
TextStyle(color: hasWarning ? BrandColors.warning : null)),
],
);
}

View File

@ -42,7 +42,7 @@ class _RootPageState extends State<RootPage>
MorePage(),
],
),
bottomNavigationBar: BottomTabBar(
bottomNavigationBar: BrandTabBar(
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_header/brand_header.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/utils/extensions/text_extension.dart';
class ServicesPage extends StatefulWidget {
ServicesPage({Key key}) : super(key: key);
@ -33,7 +33,7 @@ class _ServicesPageState extends State<ServicesPage> {
SizedBox(height: 24),
...connected.map((service) => _Card(service: service)).toList(),
if (uninitialized.isNotEmpty) ...[
Text('не подключены').body1,
BrandText.body1('не подключены'),
SizedBox(height: 30),
],
...uninitialized.map((service) => _Card(service: service)).toList()
@ -91,10 +91,10 @@ class _Card extends StatelessWidget {
child: Icon(iconData, size: 30, color: Colors.white),
),
SizedBox(height: 10),
Text(title).h2,
BrandText.h2(title),
SizedBox(height: 10),
if (service.state == ServiceStateType.uninitialized) ...[
Text(description).body1,
BrandText.body1(description),
SizedBox(height: 10),
BrandButton.text(
title: 'Подключить',
@ -102,7 +102,8 @@ class _Card extends StatelessWidget {
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_icons/brand_icons.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';
class UsersPage extends StatelessWidget {
@ -88,7 +88,7 @@ class _User extends StatelessWidget {
),
),
SizedBox(width: 20),
Text(user.login).caption,
BrandText.h4(user.login),
],
),
),
@ -271,12 +271,11 @@ class _UserDetails extends StatelessWidget {
vertical: 20,
horizontal: 15,
),
child: Text(
child: BrandText.h1(
user.login,
softWrap: true,
overflow: TextOverflow.ellipsis,
).h1,
),
)),
],
),
),
@ -286,18 +285,18 @@ class _UserDetails extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Учетная запись').small,
BrandText.small('Учетная запись'),
Container(
height: 40,
alignment: Alignment.centerLeft,
child: Text('${user.login}@example.com').caption,
child: BrandText.h4('${user.login}@example.com'),
),
SizedBox(height: 14),
Text('Пароль').small,
BrandText.small('Пароль'),
Container(
height: 40,
alignment: Alignment.centerLeft,
child: Text(user.password).caption,
child: BrandText.h4(user.password),
),
SizedBox(height: 24),
BrandDivider(),

View File

@ -1,47 +1,47 @@
library text_extension;
// library text_extension;
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:selfprivacy/config/text_themes.dart';
// 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 h3 => copyWith(style: headline3Style);
// extension TextExtension on Text {
// Text get h1 => copyWith(style: headline1Style);
// Text get h2 => copyWith(style: headline2Style);
// 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 body2 => copyWith(style: body2Style);
Text get small => copyWith(style: smallStyle);
// Text get body1 => copyWith(style: body1Style);
// Text get body2 => copyWith(style: body2Style);
// Text get small => copyWith(style: smallStyle);
Text setKey(Key key) => copyWith(key: key);
// 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);
}
}
// 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);
// }
// }