update onboarding and providers

fdroid
Kherel 2020-12-06 08:28:31 +01:00
parent 80dee9dbab
commit a112d873eb
10 changed files with 762 additions and 235 deletions

View File

@ -0,0 +1,40 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
enum ProviderTypes {
server,
domain,
backup,
}
class ProviderModel extends Equatable {
const ProviderModel({this.state, this.type});
final ServiceStateType state;
final ProviderTypes type;
ProviderModel updateState(ServiceStateType newState) => ProviderModel(
state: newState,
type: type,
);
@override
List<Object> get props => [state, type];
IconData get icon {
switch (type) {
case ProviderTypes.server:
return BrandIcons.server;
case ProviderTypes.domain:
return BrandIcons.globe;
break;
case ProviderTypes.backup:
return BrandIcons.save;
}
return null;
}
}

View File

@ -5,7 +5,6 @@ enum ServiceTypes {
messanger, messanger,
mail, mail,
passwordManager, passwordManager,
backup,
github, github,
cloud, cloud,
} }

View File

@ -20,7 +20,7 @@ void main() {
); );
} }
var _showOnbording = false; var _showOnbording = true;
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@override @override

View File

@ -99,7 +99,9 @@ class _RisedButton extends StatelessWidget {
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
child: ColoredBox( child: ColoredBox(
color: Theme.of(context).primaryColor, color: onPressed == null
? BrandColors.gray2
: Theme.of(context).primaryColor,
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(

View File

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
class DotsIndicator extends StatelessWidget {
const DotsIndicator({
Key key,
@required this.activeIndex,
@required this.count,
}) : super(key: key);
final int activeIndex;
final int count;
@override
Widget build(BuildContext context) {
var dots = List.generate(
count,
(index) => Container(
margin: EdgeInsets.symmetric(horizontal: 5, vertical: 10),
height: 10,
width: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: index == activeIndex ? BrandColors.blue : BrandColors.gray2,
),
),
);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: dots,
);
}
}

View File

@ -0,0 +1,228 @@
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/utils/extensions/text_extension.dart';
class OnboardingPage extends StatelessWidget {
const OnboardingPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
padding: brandPagePadding1,
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',
),
],
),
),
SizedBox(height: 50),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/hetzner.png'),
SizedBox(height: 10),
Text('1. Подключите сервер Hetzner').h2,
SizedBox(height: 10),
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы')
.body2,
_MockForm(
hintText: 'Hetzner API Token',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
),
],
),
),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/namecheap.png'),
SizedBox(height: 10),
Text('2. Настройте домен ').h2,
SizedBox(height: 10),
RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Зарегистрируйте домен в ',
style: body2Style,
),
BrandSpanButton.link(
text: 'NameCheap',
urlString: 'https://www.namecheap.com',
),
TextSpan(
text:
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
style: body2Style,
),
],
),
),
_MockForm(
hintText: 'Домен, например, selfprivacy.org',
submitButtonText: 'Проверить DNS',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как настроить DNS CloudFlare',
),
],
),
),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/cloudflare.png'),
SizedBox(height: 10),
Text('3. Подключите CloudFlare DNS').h2,
SizedBox(height: 10),
Text('Для управления DNS вашего домена').body2,
_MockForm(
hintText: 'CloudFlare API Token',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
),
),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/aws.png'),
SizedBox(height: 10),
Text('4. Подключите Amazon AWS для бекапа').h2,
SizedBox(height: 10),
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
.body2,
_MockForm(
hintText: 'Amazon AWS Access Key',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
),
)
],
),
);
}
void _showModal(BuildContext context, Widget widget) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return widget;
},
);
}
}
class _HowHetzner extends StatelessWidget {
const _HowHetzner({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BrandModalSheet(
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 StatelessWidget {
const _MockForm({
Key key,
@required this.hintText,
this.submitButtonText = 'Подключить',
}) : super(key: key);
final String hintText;
final String submitButtonText;
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(height: 20),
TextField(decoration: InputDecoration(hintText: hintText)),
SizedBox(height: 20),
BrandButton.rised(onPressed: () {}, title: submitButtonText),
],
);
}
}

View File

@ -5,144 +5,225 @@ 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/ui/pages/dots_indicator/dots_indicator.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class OnboardingPage extends StatelessWidget { class OnboardingPage extends StatefulWidget {
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) {
return Scaffold( var steps = getSteps();
body: ListView(
padding: brandPagePadding1, return SafeArea(
children: [ child: Scaffold(
Text('Начало').caption, body: ListView(
Text('SelfPrivacy').h1, shrinkWrap: true,
SizedBox( children: [
height: 10, Padding(
), padding: brandPagePadding1,
RichText( child: Column(
text: TextSpan( crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextSpan( Text('Начало').caption,
text: Text('SelfPrivacy').h1,
'Для устойчивости и приватности требует много учёток. Полная инструкция на ', SizedBox(
style: body2Style, height: 10,
),
BrandSpanButton.link(
text: 'selfprivacy.org/start',
urlString: 'https://selfprivacy.org/start',
),
],
),
),
SizedBox(height: 50),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/hetzner.png'),
SizedBox(height: 10),
Text('1. Подключите сервер Hetzner').h2,
SizedBox(height: 10),
Text('Здесь будут жить наши данные и SelfPrivacy-сервисы')
.body2,
_MockForm(
hintText: 'Hetzner API Token',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Как получить API Token',
),
],
),
),
BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/namecheap.png'),
SizedBox(height: 10),
Text('2. Настройте домен ').h2,
SizedBox(height: 10),
RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Зарегистрируйте домен в ',
style: body2Style,
),
BrandSpanButton.link(
text: 'NameCheap',
urlString: 'https://www.namecheap.com',
),
TextSpan(
text:
' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare',
style: body2Style,
),
],
), ),
), RichText(
_MockForm( text: TextSpan(
hintText: 'Домен, например, selfprivacy.org', children: [
submitButtonText: 'Проверить DNS', TextSpan(
), text:
SizedBox(height: 20), 'Для устойчивости и приватности требует много учёток. Полная инструкция на ',
BrandButton.text( style: body2Style,
onPressed: () {}, ),
title: 'Как настроить DNS CloudFlare', BrandSpanButton.link(
), text: 'selfprivacy.org/start',
], urlString: 'https://selfprivacy.org/start',
),
],
),
),
],
),
), ),
), Container(
BrandCard( height: 480,
child: Column( child: PageView.builder(
crossAxisAlignment: CrossAxisAlignment.start, physics: NeverScrollableScrollPhysics(),
children: [ allowImplicitScrolling: false,
Image.asset('assets/images/logos/cloudflare.png'), controller: controller,
SizedBox(height: 10), itemBuilder: (_, index) {
Text('3. Подключите CloudFlare DNS').h2, return Padding(
SizedBox(height: 10), padding: brandPagePadding2,
Text('Для управления DNS вашего домена').body2, child: steps[index],
_MockForm( );
hintText: 'CloudFlare API Token', },
), itemCount: 4,
SizedBox(height: 20), ),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
), ),
), DotsIndicator(
BrandCard( activeIndex: currentPage,
child: Column( count: steps.length,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/images/logos/aws.png'),
SizedBox(height: 10),
Text('4. Подключите Amazon AWS для бекапа').h2,
SizedBox(height: 10),
Text('IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде')
.body2,
_MockForm(
hintText: 'Amazon AWS Access Key',
),
SizedBox(height: 20),
BrandButton.text(
onPressed: () {},
title: 'Как получить API Token',
),
],
), ),
) 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) { void _showModal(BuildContext context, Widget widget) {
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
@ -153,6 +234,11 @@ class OnboardingPage extends StatelessWidget {
}, },
); );
} }
void _nextPage() => controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn,
);
} }
class _HowHetzner extends StatelessWidget { class _HowHetzner extends StatelessWidget {
@ -163,25 +249,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( Text('Как получить Hetzner API Token').h2,
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,36 +280,60 @@ class _HowHetzner extends StatelessWidget {
6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.
''', ''',
style: body1Style, style: body1Style,
), ),
], ],
),
), ),
), ],
], ),
), ),
); );
} }
} }
class _MockForm extends StatelessWidget { class _MockForm extends StatefulWidget {
const _MockForm({ const _MockForm({
Key key, Key key,
@required this.hintText, @required this.hintText,
this.submitButtonText = 'Подключить', this.submitButtonText = 'Подключить',
@required this.onPressed,
@required this.length,
}) : super(key: key); }) : super(key: key);
final String hintText; final String hintText;
final String submitButtonText; final String submitButtonText;
final int length;
final VoidCallback onPressed;
@override
__MockFormState createState() => __MockFormState();
}
class __MockFormState extends State<_MockForm> {
String text = '';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
SizedBox(height: 20), SizedBox(height: 20),
TextField(decoration: InputDecoration(hintText: hintText)), TextField(
onChanged: (value) => {
setState(() {
text = value;
})
},
decoration: InputDecoration(hintText: widget.hintText),
),
SizedBox(height: 20), SizedBox(height: 20),
BrandButton.rised(onPressed: () {}, title: submitButtonText), BrandButton.rised(
onPressed:
text.length == widget.length ? widget.onPressed ?? () {} : null,
title: widget.submitButtonText,
),
], ],
); );
} }

View File

@ -1,13 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.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_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_modal_sheet/brand_modal_sheet.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/settings/setting.dart';
import 'package:selfprivacy/utils/extensions/text_extension.dart'; import 'package:selfprivacy/utils/extensions/text_extension.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ProvidersPage extends StatefulWidget { class ProvidersPage extends StatefulWidget {
ProvidersPage({Key key}) : super(key: key); ProvidersPage({Key key}) : super(key: key);
@ -19,9 +20,11 @@ class ProvidersPage extends StatefulWidget {
class _ProvidersPageState extends State<ProvidersPage> { class _ProvidersPageState extends State<ProvidersPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final serviceCubit = context.watch<ServicesCubit>(); final cards = ProviderTypes.values
final connected = serviceCubit.state.connected; .map((type) => _Card(
final uninitialized = serviceCubit.state.uninitialized; provider:
ProviderModel(state: ServiceStateType.stable, type: type)))
.toList();
return Scaffold( return Scaffold(
appBar: PreferredSize( appBar: PreferredSize(
child: BrandHeader(title: 'Провайдеры'), child: BrandHeader(title: 'Провайдеры'),
@ -29,86 +32,161 @@ class _ProvidersPageState extends State<ProvidersPage> {
), ),
body: ListView( body: ListView(
padding: brandPagePadding2, padding: brandPagePadding2,
children: [ children: cards,
SizedBox(height: 24),
...connected.map((service) => _Card(service: service)).toList(),
if (uninitialized.isNotEmpty) ...[
Text('не подключены').body1,
SizedBox(height: 30),
],
...uninitialized.map((service) => _Card(service: service)).toList()
],
), ),
); );
} }
} }
class _Card extends StatelessWidget { class _Card extends StatelessWidget {
const _Card({Key key, @required this.service}) : super(key: key); const _Card({Key key, @required this.provider}) : super(key: key);
final Service service; final ProviderModel provider;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String title; String title;
IconData iconData; String message;
String description; String stableText;
switch (service.type) { switch (provider.type) {
case ServiceTypes.messanger: case ProviderTypes.server:
iconData = BrandIcons.messanger; title = 'Сервер';
title = 'Мессенджер'; stableText = 'В норме';
description =
'Delta Chat срфеТекст-текст описание. Если бы мне надо было обсудить что-то от чего зависит жизнь. Я бы выбрал Delta.Chat + свой почтовый сервер.';
break; break;
case ServiceTypes.mail: case ProviderTypes.domain:
iconData = BrandIcons.envelope; title = 'Домен';
title = 'Почта'; message = 'example.com';
description = 'Электронная почта для семьи или компании '; stableText = 'Домен настроен';
break; break;
case ServiceTypes.passwordManager: case ProviderTypes.backup:
iconData = BrandIcons.key; message = '22 янв 2021 14:30';
title = 'Менеджер паролей';
description = 'Надёжное хранилище для ваших паролей и ключей доступа';
break;
case ServiceTypes.github:
iconData = BrandIcons.github;
title = 'Git сервер';
description = 'Сервис для приватного хранения своих разработок';
break;
case ServiceTypes.backup:
iconData = BrandIcons.save;
title = 'Резервное копирование'; title = 'Резервное копирование';
description = 'Обеспеченье целосности и сохранности ваших данных'; stableText = 'В норме';
break;
case ServiceTypes.cloud:
iconData = BrandIcons.upload;
title = 'Файловое Облако';
description = 'Сервис для доступа к вашим файлам в любой точке мира';
break; break;
} }
return BrandCard( return GestureDetector(
onTap: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return _ProviderDetails(
provider: provider,
statusText: stableText,
);
},
),
child: BrandCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconStatusMaks(
status: provider.state,
child: Icon(provider.icon, size: 30, color: Colors.white),
),
SizedBox(height: 10),
Text(title).h2,
SizedBox(height: 10),
if (message != null) ...[
Text(message).body2,
SizedBox(height: 10),
],
if (provider.state == ServiceStateType.stable)
Text(stableText).body2,
],
),
),
);
}
}
class _ProviderDetails extends StatelessWidget {
const _ProviderDetails({
Key key,
@required this.provider,
@required this.statusText,
}) : super(key: key);
final ProviderModel provider;
final String statusText;
@override
Widget build(BuildContext context) {
String title;
switch (provider.type) {
case ProviderTypes.server:
title = 'Сервер';
break;
case ProviderTypes.domain:
title = 'Домен';
break;
case ProviderTypes.backup:
title = 'Резервное копирование';
break;
}
return BrandModalSheet(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
IconStatusMaks( Align(
status: service.state, alignment: Alignment.centerRight,
child: Icon(iconData, size: 30, color: Colors.white), child: Padding(
padding: EdgeInsets.symmetric(
vertical: 4,
horizontal: 2,
),
child: PopupMenuButton<_PopupMenuItemType>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
onSelected: (_PopupMenuItemType result) {
switch (result) {
case _PopupMenuItemType.setting:
Navigator.of(context)
.pushReplacement(materialRoute(SettingsPage()));
break;
}
},
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [
PopupMenuItem<_PopupMenuItemType>(
value: _PopupMenuItemType.setting,
child: Container(
padding: EdgeInsets.only(left: 5),
child: Text('Настройки'),
),
),
],
),
),
), ),
SizedBox(height: 10), Padding(
Text(title).h2, padding: brandPagePadding1,
SizedBox(height: 10), child: Column(
if (service.state == ServiceStateType.uninitialized) ...[ crossAxisAlignment: CrossAxisAlignment.start,
Text(description).body1, children: [
SizedBox(height: 10), SizedBox(height: 13),
BrandButton.text( IconStatusMaks(
title: 'Подключить', status: provider.state,
onPressed: () { child: Icon(provider.icon, size: 40, color: Colors.white),
context.read<ServicesCubit>().connect(service); ),
}) SizedBox(height: 10),
], Text(title).h1,
if (service.state == ServiceStateType.stable) Text('Подключен').body1, SizedBox(height: 10),
Text(statusText).body1,
SizedBox(
height: 20,
),
Text('Статусы сервера и сервис провайдера и т.д.')
],
),
)
], ],
), ),
); );
} }
} }
enum _PopupMenuItemType { setting }

View File

@ -75,11 +75,7 @@ class _Card extends StatelessWidget {
title = 'Git сервер'; title = 'Git сервер';
description = 'Сервис для приватного хранения своих разработок'; description = 'Сервис для приватного хранения своих разработок';
break; break;
case ServiceTypes.backup:
iconData = BrandIcons.save;
title = 'Резервное копирование';
description = 'Обеспеченье целосности и сохранности ваших данных';
break;
case ServiceTypes.cloud: case ServiceTypes.cloud:
iconData = BrandIcons.upload; iconData = BrandIcons.upload;
title = 'Файловое Облако'; title = 'Файловое Облако';

View File

@ -199,26 +199,60 @@ class _UserDetails extends StatelessWidget {
vertical: 4, vertical: 4,
horizontal: 2, horizontal: 2,
), ),
child: PopupMenuButton<int>( child: PopupMenuButton<PopupMenuItemType>(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
), ),
// onSelected: (WhyFarther result) { onSelected: (PopupMenuItemType result) {
// setState(() { switch (result) {
// _selection = result; case PopupMenuItemType.reset:
// }); break;
// }, case PopupMenuItemType.delete:
showDialog(
context: context,
child: AlertDialog(
title: Text('Подтверждение '),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('удалить учетную запись?'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('Отменить'),
onPressed: () {
Navigator.of(context)..pop();
},
),
TextButton(
child: Text(
'Удалить',
style: TextStyle(
color: BrandColors.red,
),
),
onPressed: () {
Navigator.of(context)..pop()..pop();
},
),
],
));
break;
}
},
icon: Icon(Icons.more_vert), icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [ itemBuilder: (BuildContext context) => [
PopupMenuItem<int>( PopupMenuItem<PopupMenuItemType>(
value: 1, value: PopupMenuItemType.reset,
child: Container( child: Container(
padding: EdgeInsets.only(left: 5), padding: EdgeInsets.only(left: 5),
child: Text('Сбросить пароль'), child: Text('Сбросить пароль'),
), ),
), ),
PopupMenuItem<int>( PopupMenuItem<PopupMenuItemType>(
value: 2, value: PopupMenuItemType.delete,
child: Container( child: Container(
padding: EdgeInsets.only(left: 5), padding: EdgeInsets.only(left: 5),
child: Text( child: Text(
@ -277,7 +311,7 @@ class _UserDetails extends StatelessWidget {
BrandDivider(), BrandDivider(),
SizedBox(height: 20), SizedBox(height: 20),
Text( Text(
'Вам был создан доступ к сервисам с логином <login> и паролем <password> к сервисам:- E-mail с адресом <username@domain.com>- Менеджер паролей: <pass.domain.com>- Файловое облако: <cloud.mydomain.com>- Видеоконференция <meet.domain.com>- Git сервер <git.mydomain.com>'), 'Вам был создан доступ к сервисам с логином <login> и паролем <password> к сервисам:- E-mail с адресом <username@domain.com>- Менеджер паролей: <pass.domain.com>- Файловое облако: <cloud.mydomain.com>- Видеоконференция <meet.domain.com>- Git сервер <git.mydomain.com>'),
], ],
), ),
) )
@ -286,3 +320,8 @@ class _UserDetails extends StatelessWidget {
); );
} }
} }
enum PopupMenuItemType {
reset,
delete,
}