Мобильные приложения окружают нас повсюду - от игр и социальных сетей до образовательных программ и инструментов продуктивности. Каждый день миллионы людей используют смартфоны и планшеты, что делает мобильную разработку одной из самых востребованных и перспективных областей программирования. Для детей создание мобильных приложений - это возможность воплотить свои идеи в жизнь и поделиться ими с миллионами пользователей по всему миру.
Мобильная разработка - это процесс создания программного обеспечения для мобильных устройств: смартфонов, планшетов, умных часов и других гаджетов. Современные мобильные приложения могут быть невероятно мощными, используя камеру, GPS, сенсоры, интернет и множество других возможностей устройства.
Почему мобильная разработка идеальна для детей:
Результат сразу виден на экране устройства
Сенсорное управление понятно каждому ребенку
Возможность использовать камеру, микрофон, акселерометр
Мгновенная реакция на действия пользователя
Интерактивный дизайн - кнопки, анимации, переходы
Мультимедиа - звуки, музыка, видео, изображения
Геймификация - элементы игр в любом приложении
Персонализация - настройки под пользователя
Навыки, востребованные на рынке труда
Возможность заработка через App Store и Google Play
Портфолио для поступления в университет
Развитие предпринимательских навыков
MIT App Inventor - первые шаги в мобильной разработке
MIT App Inventor - это визуальная среда разработки, созданная Массачусетским технологическим институтом специально для обучения программированию. Она использует блочный интерфейс, похожий на Scratch, но создает настоящие Android-приложения.
Основные преимущества App Inventor:
Не требует знания текстового программирования
Работает в браузере - не нужно ничего устанавливать
Создает реальные APK-файлы для Android
Доступ к датчикам, камере, GPS смартфона
Возможность публикации в Google Play
Проект: Приложение "Мой питомец"
Создадим виртуального питомца, за которым нужно ухаживать:
=== ДИЗАЙН ИНТЕРФЕЙСА ===
Компоненты экрана:
1. Image (картинка питомца) - имя: "ИзображениеПитомца"
2. Label (имя питомца) - имя: "ИмяПитомца"
3. ProgressBar (уровень голода) - имя: "УровеньГолода"
4. ProgressBar (уровень счастья) - имя: "УровеньСчастья"
5. Button (покормить) - имя: "КнопкаПокормить"
6. Button (поиграть) - имя: "КнопкаПоиграть"
7. Button (уложить спать) - имя: "КнопкаСпать"
Невидимые компоненты:
- Clock (таймер) - имя: "Таймер"
- TinyDB (база данных) - имя: "БазаДанных"
- Sound (звуковые эффекты) - имя: "Звук"
Логика приложения (блоки):
=== ИНИЦИАЛИЗАЦИЯ ===
Когда Screen1.Initialize:
Установить ИмяПитомца.Text = "Мой Котик"
Установить УровеньГолода.Progress = БазаДанных.GetValue("голод", 50)
Установить УровеньСчастья.Progress = БазаДанных.GetValue("счастье", 50)
Установить Таймер.TimerEnabled = true
=== КОРМЛЕНИЕ ===
Когда КнопкаПокормить.Click:
Если УровеньГолода.Progress < 100:
Установить УровеньГолода.Progress = УровеньГолода.Progress + 20
Установить ИмяПитомца.Text = "Мур-мур! Вкусно!"
Звук.Play("eating.mp3")
БазаДанных.StoreValue("голод", УровеньГолода.Progress)
Иначе:
Установить ИмяПитомца.Text = "Я уже сыт!"
=== ИГРА ===
Когда КнопкаПоиграть.Click:
Если УровеньСчастья.Progress < 100:
Установить УровеньСчастья.Progress = УровеньСчастья.Progress + 15
Установить ИмяПитомца.Text = "Ура! Играем!"
Звук.Play("play.mp3")
БазаДанных.StoreValue("счастье", УровеньСчастья.Progress)
Иначе:
Установить ИмяПитомца.Text = "Я уже очень счастлив!"
=== СОН ===
Когда КнопкаСпать.Click:
Установить ИмяПитомца.Text = "Zzz... Сплю..."
Установить ИзображениеПитомца.Picture = "cat_sleeping.png"
=== АВТОМАТИЧЕСКОЕ УМЕНЬШЕНИЕ ПОКАЗАТЕЛЕЙ ===
Когда Таймер.Timer: (каждые 60 секунд)
Если УровеньГолода.Progress > 0:
Установить УровеньГолода.Progress = УровеньГолода.Progress - 5
Если УровеньСчастья.Progress > 0:
Установить УровеньСчастья.Progress = УровеньСчастья.Progress - 3
БазаДанных.StoreValue("голод", УровеньГолода.Progress)
БазаДанных.StoreValue("счастье", УровеньСчастья.Progress)
Если УровеньГолода.Progress < 20:
Установить ИмяПитомца.Text = "Я голодный! Покорми меня!"
Если УровеньСчастья.Progress < 20:
Установить ИмяПитомца.Text = "Мне скучно! Поиграй со мной!"
Thunkable - более продвинутые возможности
Thunkable - это современная платформа для создания мобильных приложений с drag-and-drop интерфейсом, которая поддерживает как Android, так и iOS.
Проект: Приложение "Калькулятор оценок"
=== ИНТЕРФЕЙС ===
Экран "Главный":
- TextInput (название предмета) - "ВводПредмета"
- TextInput (оценка) - "ВводОценки"
- Button (добавить оценку) - "КнопкаДобавить"
- ListViewer (список оценок) - "СписокОценок"
- Label (средний балл) - "СреднийБалл"
- Button (очистить все) - "КнопкаОчистить"
=== ДАННЫЕ ===
Переменные:
- СписокПредметов (список)
- СписокБаллов (список)
- ОбщийБалл (число)
=== ЛОГИКА ===
При нажатии КнопкаДобавить:
Если ВводПредмета.Text ≠ пусто И ВводОценки.Text ≠ пусто:
Добавить в СписокПредметов: ВводПредмета.Text
Добавить в СписокБаллов: преобразовать ВводОценки.Text в число
Обновить СписокОценок.Items:
Для каждого элемента i в СписокПредметов:
элемент i из СписокПредметов + ": " + элемент i из СписокБаллов
Вычислить ОбщийБалл = среднее арифметическое СписокБаллов
Установить СреднийБалл.Text = "Средний балл: " + ОбщийБалл
Очистить ВводПредмета.Text и ВводОценки.Text
При нажатии КнопкаОчистить:
Очистить СписокПредметов
Очистить СписокБаллов
Очистить СписокОценок.Items
Установить СреднийБалл.Text = "Средний балл: 0"
Flutter - это современный фреймворк от Google для создания кроссплатформенных мобильных приложений. Одна кодовая база работает на Android и iOS.
Основы Dart
Dart - язык программирования, используемый во Flutter. Он похож на Java и JavaScript, но проще для изучения.
// Основы синтаксиса Dart
void main() {
print('Привет, мобильный мир!');
// Переменные
String имя = 'Анна';
int возраст = 12;
double рост = 1.45;
bool учитсяВШколе = true;
// Списки
List<String> хобби = ['программирование', 'рисование', 'чтение'];
// Словари
Map<String, dynamic> ученик = {
'имя': имя,
'возраст': возраст,
'хобби': хобби,
};
// Функции
String представиться() {
return 'Привет! Меня зовут $имя, мне $возраст лет.';
}
print(представиться());
// Циклы
for (String увлечение in хобби) {
print('Мне нравится $увлечение');
}
// Условия
if (возраст < 18) {
print('Я еще ребенок');
} else {
print('Я взрослый');
}
}
// Классы
class Ученик {
String имя;
int возраст;
List<int> оценки;
// Конструктор
Ученик(this.имя, this.возраст, this.оценки);
// Методы
double вычислитьСреднийБалл() {
if (оценки.isEmpty) return 0;
int сумма = оценки.reduce((a, b) => a + b);
return сумма / оценки.length;
}
void добавитьОценку(int оценка) {
if (оценка >= 1 && оценка <= 5) {
оценки.add(оценка);
print('Добавлена оценка $оценка для $имя');
} else {
print('Неверная оценка! Должна быть от 1 до 5');
}
}
void показатьИнформацию() {
print('Ученик: $имя');
print('Возраст: $возраст');
print('Оценки: $оценки');
print('Средний балл: ${вычислитьСреднийБалл().toStringAsFixed(2)}');
}
}
// Пример использования класса
void примерИспользования() {
Ученик анна = Ученик('Анна', 12, [4, 5, 3, 5]);
анна.показатьИнформацию();
анна.добавитьОценку(5);
анна.показатьИнформацию();
}
Первое Flutter приложение
import 'package:flutter/material.dart';
void main() {
runApp(МоеПриложение());
}
class МоеПриложение extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Мое первое приложение',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Roboto',
),
home: ГлавнаяСтраница(),
);
}
}
class ГлавнаяСтраница extends StatefulWidget {
@override
_ГлавнаяСтраницаState createState() => _ГлавнаяСтраницаState();
}
class _ГлавнаяСтраницаState extends State<ГлавнаяСтраница> {
int _счетчик = 0;
String _сообщение = 'Нажми кнопку!';
void _увеличитьСчетчик() {
setState(() {
_счетчик++;
if (_счетчик == 1) {
_сообщение = 'Отлично! Первое нажатие!';
} else if (_счетчик == 10) {
_сообщение = 'Ого! Уже 10 нажатий!';
} else if (_счетчик == 50) {
_сообщение = 'Вау! 50 нажатий! Ты настоящий кликер!';
} else if (_счетчик % 100 == 0) {
_сообщение = 'Невероятно! $_счетчик нажатий!';
} else {
_сообщение = 'Продолжай нажимать!';
}
});
}
void _сброситьСчетчик() {
setState(() {
_счетчик = 0;
_сообщение = 'Счетчик сброшен! Начинай заново!';
});
}
Color _получитьЦветКнопки() {
if (_счетчик < 10) return Colors.blue;
if (_счетчик < 50) return Colors.green;
if (_счетчик < 100) return Colors.orange;
return Colors.purple;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Счетчик нажатий'),
backgroundColor: _получитьЦветКнопки(),
elevation: 4,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.blue.shade50, Colors.white],
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.touch_app,
size: 100,
color: _получитьЦветКнопки(),
),
SizedBox(height: 20),
Text(
_сообщение,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
textAlign: TextAlign.center,
),
SizedBox(height: 30),
Text(
'Количество нажатий:',
style: TextStyle(
fontSize: 20,
color: Colors.grey[600],
),
),
Text(
'$_счетчик',
style: TextStyle(
fontSize: 72,
fontWeight: FontWeight.bold,
color: _получитьЦветКнопки(),
),
),
SizedBox(height: 40),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton.extended(
onPressed: _увеличитьСчетчик,
label: Text('Нажми!'),
icon: Icon(Icons.add),
backgroundColor: _получитьЦветКнопки(),
),
FloatingActionButton.extended(
onPressed: _сброситьСчетчик,
label: Text('Сброс'),
icon: Icon(Icons.refresh),
backgroundColor: Colors.red,
),
],
),
],
),
),
),
);
}
}
Давайте создадим практическое приложение, которое поможет детям отслеживать свое настроение и эмоции.
Функциональность:
Запись ежедневного настроения (эмодзи)
Добавление заметок о дне
Просмотр истории настроений
Статистика эмоций за месяц
Мотивирующие цитаты
Структура приложения:
lib/
├── main.dart // Точка входа
├── models/
│ ├── mood_entry.dart // Модель записи настроения
│ └── database_helper.dart // Работа с базой данных
├── screens/
│ ├── home_screen.dart // Главный экран
│ ├── add_mood_screen.dart // Добавление настроения
│ ├── history_screen.dart // История записей
│ └── stats_screen.dart // Статистика
└── widgets/
├── mood_card.dart // Карточка настроения
└── mood_chart.dart // График настроений
// models/mood_entry.dart
class ЗаписьНастроения {
int? id;
DateTime дата;
String эмодзи;
String название;
String? заметка;
int рейтинг; // 1-5, где 5 - очень хорошо
ЗаписьНастроения({
this.id,
required this.дата,
required this.эмодзи,
required this.название,
this.заметка,
required this.рейтинг,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'дата': дата.millisecondsSinceEpoch,
'эмодзи': эмодзи,
'название': название,
'заметка': заметка,
'рейтинг': рейтинг,
};
}
static ЗаписьНастроения fromMap(Map<String, dynamic> map) {
return ЗаписьНастроения(
id: map['id'],
дата: DateTime.fromMillisecondsSinceEpoch(map['дата']),
эмодзи: map['эмодзи'],
название: map['название'],
заметка: map['заметка'],
рейтинг: map['рейтинг'],
);
}
String получитьОписание() {
String описание = '$эмодзи $название';
if (заметка != null && заметка!.isNotEmpty) {
описание += '\n$заметка';
}
return описание;
}
}
// Предустановленные настроения
class ПредустановленныеНастроения {
static List<Map<String, dynamic>> получить() {
return [
{'эмодзи': '😊', 'название': 'Счастлив', 'рейтинг': 5},
{'эмодзи': '😄', 'название': 'В восторге', 'рейтинг': 5},
{'эмодзи': '😌', 'название': 'Спокоен', 'рейтинг': 4},
{'эмодзи': '🤔', 'название': 'Задумчив', 'рейтинг': 3},
{'эмодзи': '😕', 'название': 'Расстроен', 'рейтинг': 2},
{'эмодзи': '😢', 'название': 'Грустен', 'рейтинг': 2},
{'эмодзи': '😴', 'название': 'Устал', 'рейтинг': 2},
{'эмодзи': '😤', 'название': 'Сердит', 'рейтинг': 1},
{'эмодзи': '🤒', 'название': 'Болен', 'рейтинг': 1},
{'эмодзи': '🤗', 'название': 'Обнимашки', 'рейтинг': 4},
];
}
}
// models/database_helper.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'mood_entry.dart';
class ПомощникБазыДанных {
static Database? _база;
static const String _имяТаблицы = 'записи_настроений';
static Future<Database> получитьБазу() async {
if (_база != null) return _база!;
_база = await _инициализироватьБазу();
return _база!;
}
static Future<Database> _инициализироватьБазу() async {
String путь = join(await getDatabasesPath(), 'mood_diary.db');
return await openDatabase(
путь,
version: 1,
onCreate: _создатьТаблицы,
);
}
static Future<void> _создатьТаблицы(Database db, int version) async {
await db.execute('''
CREATE TABLE $_имяТаблицы (
id INTEGER PRIMARY KEY AUTOINCREMENT,
дата INTEGER NOT NULL,
эмодзи TEXT NOT NULL,
название TEXT NOT NULL,
заметка TEXT,
рейтинг INTEGER NOT NULL
)
''');
}
static Future<int> добавитьЗапись(ЗаписьНастроения запись) async {
final db = await получитьБазу();
return await db.insert(_имяТаблицы, запись.toMap());
}
static Future<List<ЗаписьНастроения>> получитьВсеЗаписи() async {
final db = await получитьБазу();
final List<Map<String, dynamic>> карты = await db.query(
_имяТаблицы,
orderBy: 'дата DESC',
);
return карты.map((карта) => ЗаписьНастроения.fromMap(карта)).toList();
}
static Future<List<ЗаписьНастроения>> получитьЗаписиЗаПериод(
DateTime начало,
DateTime конец,
) async {
final db = await получитьБазу();
final List<Map<String, dynamic>> карты = await db.query(
_имяТаблицы,
where: 'дата BETWEEN ? AND ?',
whereArgs: [
начало.millisecondsSinceEpoch,
конец.millisecondsSinceEpoch,
],
orderBy: 'дата DESC',
);
return карты.map((карта) => ЗаписьНастроения.fromMap(карта)).toList();
}
static Future<Map<String, int>> получитьСтатистику() async {
final записи = await получитьВсеЗаписи();
Map<String, int> статистика = {};
for (var запись in записи) {
статистика[запись.название] = (статистика[запись.название] ?? 0) + 1;
}
return статистика;
}
static Future<double> получитьСреднийРейтинг() async {
final записи = await получитьВсеЗаписи();
if (записи.isEmpty) return 0.0;
int сумма = записи.fold(0, (sum, запись) => sum + запись.рейтинг);
return сумма / записи.length;
}
}
// screens/add_mood_screen.dart
import 'package:flutter/material.dart';
import '../models/mood_entry.dart';
import '../models/database_helper.dart';
class ЭкранДобавленияНастроения extends StatefulWidget {
@override
_ЭкранДобавленияНастроенияState createState() => _ЭкранДобавленияНастроенияState();
}
class _ЭкранДобавленияНастроенияState extends State<ЭкранДобавленияНастроения> {
String? _выбранноеЭмодзи;
String? _выбранноеНазвание;
int? _выбранныйРейтинг;
final TextEditingController _контроллерЗаметки = TextEditingController();
final List<Map<String, dynamic>> _настроения = ПредустановленныеНастроения.получить();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Как дела сегодня?'),
backgroundColor: Colors.purple,
elevation: 0,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.purple.shade50, Colors.white],
),
),
child: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_построитьЗаголовок(),
SizedBox(height: 24),
_построитьСеткуНастроений(),
SizedBox(height: 24),
_построитьПолеЗаметки(),
SizedBox(height: 32),
_построитьКнопкуСохранения(),
],
),
),
),
);
}
Widget _построитьЗаголовок() {
return Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Icon(
Icons.mood,
size: 48,
color: Colors.purple,
),
SizedBox(height: 8),
Text(
'Выбери свое настроение',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.purple.shade700,
),
),
Text(
DateTime.now().day.toString() + ' ' + _получитьНазваниеМесяца(),
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
],
),
),
);
}
Widget _построитьСеткуНастроений() {
return Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Мое настроение:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _настроения.length,
itemBuilder: (context, index) {
final настроение = _настроения[index];
final выбрано = _выбранноеЭмодзи == настроение['эмодзи'];
return GestureDetector(
onTap: () {
setState(() {
_выбранноеЭмодзи = настроение['эмодзи'];
_выбранноеНазвание = настроение['название'];
_выбранныйРейтинг = настроение['рейтинг'];
});
},
child: Container(
decoration: BoxDecoration(
color: выбрано ? Colors.purple.shade100 : Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: выбрано ? Colors.purple : Colors.grey.shade300,
width: выбрано ? 2 : 1,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
настроение['эмодзи'],
style: TextStyle(fontSize: 32),
),
SizedBox(height: 4),
Text(
настроение['название'],
style: TextStyle(
fontSize: 12,
fontWeight: выбрано ? FontWeight.bold : FontWeight.normal,
color: выбрано ? Colors.purple.shade700 : Colors.grey[700],
),
textAlign: TextAlign.center,
),
],
),
),
);
},
),
],
),
),
);
}
Widget _построитьПолеЗаметки() {
return Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Что особенного произошло сегодня?',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
TextField(
controller: _контроллерЗаметки,
maxLines: 4,
decoration: InputDecoration(
hintText: 'Расскажи о своем дне...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.purple),
),
),
),
],
),
),
);
}
Widget _построитьКнопкуСохранения() {
return SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(
onPressed: _выбранноеЭмодзи != null ? _сохранитьЗапись : null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 2,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.save, color: Colors.white),
SizedBox(width: 8),
Text(
'Сохранить настроение',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
);
}
Future<void> _сохранитьЗапись() async {
if (_выбранноеЭмодзи == null || _выбранноеНазвание == null || _выбранныйРейтинг == null) {
return;
}
final запись = ЗаписьНастроения(
дата: DateTime.now(),
эмодзи: _выбранноеЭмодзи!,
название: _выбранноеНазвание!,
заметка: _контроллерЗаметки.text.isEmpty ? null : _контроллерЗаметки.text,
рейтинг: _выбранныйРейтинг!,
);
await ПомощникБазыДанных.добавитьЗапись(запись);
// Показываем подтверждение
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Настроение сохранено! $_выбранноеЭмодзи'),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
// Возвращаемся на главный экран
Navigator.pop(context, true);
}
String _получитьНазваниеМесяца() {
final месяцы = [
'января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'
];
return месяцы[DateTime.now().month - 1];
}
@override
void dispose() {
_контроллерЗаметки.dispose();
super.dispose();
}
}
// screens/home_screen.dart
import 'package:flutter/material.dart';
import '../models/mood_entry.dart';
import '../models/database_helper.dart';
import 'add_mood_screen.dart';
import 'history_screen.dart';
import 'stats_screen.dart';
class ГлавныйЭкран extends StatefulWidget {
@override
_ГлавныйЭкранState createState() => _ГлавныйЭкранState();
}
class _ГлавныйЭкранState extends State<ГлавныйЭкран> {
ЗаписьНастроения? _сегодняшняяЗапись;
double _среднийРейтинг = 0.0;
int _общееКоличествоЗаписей = 0;
@override
void initState() {
super.initState();
_загрузитьДанные();
}
Future<void> _загрузитьДанные() async {
final записи = await ПомощникБазыДанных.получитьВсеЗаписи();
final среднийРейтинг = await ПомощникБазыДанных.получитьСреднийРейтинг();
// Проверяем, есть ли запись за сегодня
final сегодня = DateTime.now();
final сегодняшняяЗапись = записи.where((запись) {
return запись.дата.year == сегодня.year &&
запись.дата.month == сегодня.month &&
запись.дата.day == сегодня.day;
}).isNotEmpty ? записи.first : null;
setState(() {
_сегодняшняяЗапись = сегодняшняяЗапись;
_среднийРейтинг = среднийРейтинг;
_общееКоличествоЗаписей = записи.length;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Дневник настроения'),
backgroundColor: Colors.purple,
elevation: 0,
actions: [
IconButton(
icon: Icon(Icons.analytics),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ЭкранСтатистики()),
);
},
),
],
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.purple.shade50, Colors.white],
),
),
child: RefreshIndicator(
onRefresh: _загрузитьДанные,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_построитьПриветствие(),
SizedBox(height: 24),
_построитьСегодняшнееНастроение(),
SizedBox(height: 24),
_построитьСтатистику(),
SizedBox(height: 24),
_построитьБыстрыеДействия(),
SizedBox(height: 24),
_построитьМотивирующуюЦитату(),
],
),
),
),
),
);
}
Widget _построитьПриветствие() {
final час = DateTime.now().hour;
String приветствие;
if (час < 12) {
приветствие = 'Доброе утро!';
} else if (час < 17) {
приветствие = 'Добрый день!';
} else {
приветствие = 'Добрый вечер!';
}
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: [Colors.purple.shade300, Colors.purple.shade500],
),
),
child: Column(
children: [
Text(
приветствие,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 8),
Text(
'Как прошел твой день?',
style: TextStyle(
fontSize: 16,
color: Colors.white.withOpacity(0.9),
),
),
],
),
),
);
}
Widget _построитьСегодняшнееНастроение() {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Сегодня',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 16),
if (_сегодняшняяЗапись != null) ...[
Row(
children: [
Text(
_сегодняшняяЗапись!.эмодзи,
style: TextStyle(fontSize: 48),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_сегодняшняяЗапись!.название,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
if (_сегодняшняяЗапись!.заметка != null) ...[
SizedBox(height: 4),
Text(
_сегодняшняяЗапись!.заметка!,
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
] else ...[
Container(
width: double.infinity,
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
child: Column(
children: [
Icon(
Icons.mood_outlined,
size: 48,
color: Colors.grey[400],
),
SizedBox(height: 8),
Text(
'Еще не записано',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _добавитьНастроение,
icon: Icon(Icons.add),
label: Text('Записать настроение'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
],
),
),
],
],
),
),
);
}
Widget _построитьСтатистику() {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Моя статистика',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: _построитьСтатистическуюКарточку(
'Записей',
_общееКоличествоЗаписей.toString(),
Icons.book,
Colors.blue,
),
),
SizedBox(width: 12),
Expanded(
child: _построитьСтатистическуюКарточку(
'Средний балл',
_среднийРейтинг.toStringAsFixed(1),
Icons.star,
Colors.orange,
),
),
],
),
],
),
),
);
}
Widget _построитьСтатистическуюКарточку(
String заголовок,
String значение,
IconData иконка,
Color цвет,
) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: цвет.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Icon(иконка, color: цвет, size: 24),
SizedBox(height: 8),
Text(
значение,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: цвет,
),
),
Text(
заголовок,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
);
}
Widget _построитьБыстрыеДействия() {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Быстрые действия',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: _построитьКнопкуДействия(
'Добавить настроение',
Icons.add_mood,
Colors.purple,
_добавитьНастроение,
),
),
SizedBox(width: 12),
Expanded(
child: _построитьКнопкуДействия(
'История',
Icons.history,
Colors.blue,
_открытьИсторию,
),
),
],
),
],
),
),
);
}
Widget _построитьКнопкуДействия(
String текст,
IconData иконка,
Color цвет,
VoidCallback действие,
) {
return ElevatedButton(
onPressed: действие,
style: ElevatedButton.styleFrom(
backgroundColor: цвет,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
),
child: Column(
children: [
Icon(иконка, size: 24),
SizedBox(height: 4),
Text(
текст,
style: TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _построитьМотивирующуюЦитату() {
final цитаты = [
'Каждый день - новая возможность быть счастливым! 🌟',
'Твои эмоции важны. Записывай их! 📝',
'Маленькие радости делают жизнь прекрасной ✨',
'Помни: после дождичка всегда выходит солнышко! ☀️',
'Ты молодец, что следишь за своим настроением! 👍',
];
final случайнаяЦитата = цитаты[DateTime.now().millisecond % цитаты.length];
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: [Colors.orange.shade100, Colors.yellow.shade100],
),
),
child: Column(
children: [
Icon(
Icons.lightbulb,
color: Colors.orange,
size: 32,
),
SizedBox(height: 12),
Text(
случайнаяЦитата,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.orange.shade800,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Future<void> _добавитьНастроение() async {
final результат = await Navigator.push<bool>(
context,
MaterialPageRoute(builder: (context) => ЭкранДобавленияНастроения()),
);
if (результат == true) {
_загрузитьДанные();
}
}
void _открытьИсторию() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ЭкранИстории()),
);
}
}
Современные мобильные приложения часто работают с интернет-сервисами. Давайте создадим функцию для получения вдохновляющих цитат из API.
// services/quotes_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Цитата {
final String текст;
final String автор;
Цитата({required this.текст, required this.автор});
factory Цитата.fromJson(Map<String, dynamic> json) {
return Цитата(
текст: json['text'] ?? json['content'] ?? '',
автор: json['author'] ?? 'Неизвестен',
);
}
}
class СервисЦитат {
static const String _базовыйUrl = 'https://api.quotable.io';
static Future<Цитата?> получитьСлучайнуюЦитату() async {
try {
final ответ = await http.get(
Uri.parse('$_базовыйUrl/random?tags=motivational,inspirational'),
headers: {'Content-Type': 'application/json'},
).timeout(Duration(seconds: 10));
if (ответ.statusCode == 200) {
final данные = json.decode(ответ.body);
return Цитата.fromJson(данные);
} else {
print('Ошибка API: ${ответ.statusCode}');
return _получитьЗапаснуюЦитату();
}
} catch (e) {
print('Ошибка сети: $e');
return _получитьЗапаснуюЦитату();
}
}
static Future<List<Цитата>> получитьЦитатыПоТеме(String тема) async {
try {
final ответ = await http.get(
Uri.parse('$_базовыйUrl/quotes?tags=$тема&limit=10'),
).timeout(Duration(seconds: 15));
if (ответ.statusCode == 200) {
final данные = json.decode(ответ.body);
final List<dynamic> результаты = данные['results'] ?? [];
return результаты
.map((цитата) => Цитата.fromJson(цитата))
.toList();
}
return _получитьЗапасныеЦитаты();
} catch (e) {
print('Ошибка при получении цитат по теме: $e');
return _получитьЗапасныеЦитаты();
}
}
static Цитата _получитьЗапаснуюЦитату() {
final запасныеЦитаты = [
Цитата(текст: 'Каждый день - это новая возможность стать лучше', автор: 'Мудрость'),
Цитата(текст: 'Счастье - это не пункт назначения, это способ путешествия', автор: 'Жизненная мудрость'),
Цитата(текст: 'Верь в себя, и ты сможешь достичь всего', автор: 'Мотивация'),
];
return запасныеЦитаты[DateTime.now().millisecond % запасныеЦитаты.length];
}
static List<Цитата> _получитьЗапасныеЦитаты() {
return [
Цитата(текст: 'Успех - это сумма маленьких усилий, повторяемых изо дня в день', автор: 'Роберт Кольер'),
Цитата(текст: 'Не бойся расти медленно, бойся стоять на месте', автор: 'Китайская пословица'),
Цитата(текст: 'Лучшее время посадить дерево было 20 лет назад. Второе лучшее время - сейчас', автор: 'Китайская пословица'),
];
}
}
// Виджет для отображения цитаты
class ВиджетЦитаты extends StatefulWidget {
@override
_ВиджетЦитатыState createState() => _ВиджетЦитатыState();
}
class _ВиджетЦитатыState extends State<ВиджетЦитаты> {
Цитата? _текущаяЦитата;
bool _загружается = false;
@override
void initState() {
super.initState();
_загрузитьЦитату();
}
Future<void> _загрузитьЦитату() async {
setState(() {
_загружается = true;
});
final цитата = await СервисЦитат.получитьСлучайнуюЦитату();
setState(() {
_текущаяЦитата = цитата;
_загружается = false;
});
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 3,
margin: EdgeInsets.all(16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: [Colors.blue.shade50, Colors.indigo.shade50],
),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.format_quote, color: Colors.indigo, size: 28),
SizedBox(width: 8),
Text(
'Цитата дня',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.indigo.shade700,
),
),
Spacer(),
IconButton(
onPressed: _загружается ? null : _загрузитьЦитату,
icon: _загружается
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Icon(Icons.refresh, color: Colors.indigo),
),
],
),
SizedBox(height: 16),
if (_текущаяЦитата != null) ...[
Text(
'"${_текущаяЦитата!.текст}"',
style: TextStyle(
fontSize: 16,
fontStyle: FontStyle.italic,
color: Colors.grey[800],
height: 1.4,
),
textAlign: TextAlign.center,
),
SizedBox(height: 12),
Text(
'— ${_текущаяЦитата!.автор}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.indigo.shade600,
),
),
] else if (_загружается) ...[
CircularProgressIndicator(),
SizedBox(height: 8),
Text('Загружаем вдохновение...'),
] else ...[
Text('Не удалось загрузить цитату'),
ElevatedButton(
onPressed: _загрузитьЦитату,
child: Text('Попробовать снова'),
),
],
],
),
),
);
}
}
Мобильные приложения могут отправлять уведомления пользователям, даже когда приложение закрыто.
// services/notification_service.dart
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
class СервисУведомлений {
static final FlutterLocalNotificationsPlugin _плагинУведомлений =
FlutterLocalNotificationsPlugin();
static Future<void> инициализировать() async {
tz.initializeTimeZones();
// Настройки для Android
const настройкиAndroid = AndroidInitializationSettings('@mipmap/ic_launcher');
// Настройки для iOS
const настройкиIOS = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
const настройкиИнициализации = InitializationSettings(
android: настройкиAndroid,
iOS: настройкиIOS,
);
await _плагинУведомлений.initialize(
настройкиИнициализации,
onDidReceiveNotificationResponse: _приНажатииНаУведомление,
);
}
static Future<void> запроситьРазрешения() async {
await _плагинУведомлений
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
}
static Future<void> отправитьНапоминание(
String заголовок,
String текст,
DateTime время,
) async {
const детали = NotificationDetails(
android: AndroidNotificationDetails(
'mood_reminders',
'Напоминания о настроении',
channelDescription: 'Напоминания записать свое настроение',
importance: Importance.high,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
),
iOS: DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
);
await _плагинУведомлений.zonedSchedule(
0,
заголовок,
текст,
tz.TZDateTime.from(время, tz.local),
детали,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
}
static Future<void> настроитьЕжедневноеНапоминание(
int час,
int минута,
) async {
// Отменяем предыдущие напоминания
await _плагинУведомлений.cancelAll();
const детали = NotificationDetails(
android: AndroidNotificationDetails(
'daily_mood_reminder',
'Ежедневные напоминания',
channelDescription: 'Ежедневные напоминания о записи настроения',
importance: Importance.high,
priority: Priority.high,
icon: '@drawable/mood_icon',
),
);
// Планируем на каждый день
for (int день = 0; день < 7; день++) {
await _плагинУведомлений.zonedSchedule(
день,
'Время записать настроение! 😊',
'Как прошел твой день? Поделись своими эмоциями!',
_следующийВторник(час, минута).add(Duration(days: день)),
детали,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
}
static tz.TZDateTime _следующийВторник(int час, int минута) {
tz.TZDateTime сейчас = tz.TZDateTime.now(tz.local);
tz.TZDateTime запланированноеВремя = tz.TZDateTime(
tz.local,
сейчас.year,
сейчас.month,
сейчас.day,
час,
минута,
);
if (запланированноеВремя.isBefore(сейчас)) {
запланированноеВремя = запланированноеВремя.add(Duration(days: 1));
}
return запланированноеВремя;
}
static void _приНажатииНаУведомление(NotificationResponse ответ) {
// Обработка нажатия на уведомление
print('Пользователь нажал на уведомление: ${ответ.payload}');
}
static Future<void> отменитьВсеУведомления() async {
await _плагинУведомлений.cancelAll();
}
}
// Виджет настройки уведомлений
class НастройкиУведомлений extends StatefulWidget {
@override
_НастройкиУведомленийState createState() => _НастройкиУведомленийState();
}
class _НастройкиУведомленийState extends State<НастройкиУведомлений> {
bool _уведомленияВключены = false;
TimeOfDay _времяНапоминания = TimeOfDay(hour: 20, minute: 0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Настройки уведомлений'),
backgroundColor: Colors.purple,
),
body: ListView(
padding: EdgeInsets.all(16),
children: [
Card(
child: SwitchListTile(
title: Text('Ежедневные напоминания'),
subtitle: Text('Получать напоминания о записи настроения'),
value: _уведомленияВключены,
onChanged: (значение) {
setState(() {
_уведомленияВключены = значение;
});
if (значение) {
СервисУведомлений.настроитьЕжедневноеНапоминание(
_времяНапоминания.hour,
_времяНапоминания.minute,
);
} else {
СервисУведомлений.отменитьВсеУведомления();
}
},
),
),
if (_уведомленияВключены) ...[
SizedBox(height: 16),
Card(
child: ListTile(
title: Text('Время напоминания'),
subtitle: Text(_времяНапоминания.format(context)),
trailing: Icon(Icons.access_time),
onTap: () async {
final времяНапоминания = await showTimePicker(
context: context,
initialTime: _времяНапоминания,
);
if (времяНапоминания != null) {
setState(() {
_времяНапоминания = времяНапоминания;
});
// Обновляем напоминания с новым временем
СервисУведомлений.настроитьЕжедневноеНапоминание(
_времяНапоминания.hour,
_времяНапоминания.minute,
);
}
},
),
),
],
SizedBox(height: 24),
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'О уведомлениях',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Ежедневные напоминания помогут тебе не забывать записывать свое настроение. '
'Это поможет лучше понимать свои эмоции и отслеживать изменения настроения.',
style: TextStyle(color: Colors.grey[600]),
),
],
),
),
),
],
),
);
}
}
# pubspec.yaml - настройки приложения
name: mood_diary
description: Дневник настроения для детей
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
flutter: ">=3.10.0"
dependencies:
flutter:
sdk: flutter
# База данных
sqflite: ^2.3.0
path: ^1.8.3
# HTTP запросы
http: ^1.1.0
# Уведомления
flutter_local_notifications: ^16.1.0
timezone: ^0.9.2
# Состояние приложения
provider: ^6.0.5
# Даты
intl: ^0.18.1
# Иконки
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/sounds/
fonts:
- family: Roboto
fonts:
- asset: fonts/Roboto-Regular.ttf
- asset: fonts/Roboto-Bold.ttf
weight: 700
// Создание адаптивной иконки для Android
// android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (192x192)
// android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (144x144)
// android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (96x96)
// android/app/src/main/res/mipmap-hdpi/ic_launcher.png (72x72)
// android/app/src/main/res/mipmap-mdpi/ic_launcher.png (48x48)
// Для iOS - ios/Runner/Assets.xcassets/AppIcon.appiconset/
// Различные размеры от 20x20 до 1024x1024
<!-- android/app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Разрешения -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<application
android:name="${applicationName}"
android:label="Дневник настроения"
android:icon="@mipmap/ic_launcher"
android:exported="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
# Создание ключа для подписи
keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
# android/key.properties
storePassword=ваш_пароль
keyPassword=ваш_пароль
keyAlias=key
storeFile=путь_к_файлу/key.jks
// android/app/build.gradle
android {
...
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
# Очистка проекта
flutter clean
# Получение зависимостей
flutter pub get
# Сборка APK для Android
flutter build apk --release
# Сборка AAB (предпочтительно для Google Play)
flutter build appbundle --release
# Сборка для iOS
flutter build ios --release
1. Freemium модель
// Пример реализации премиум функций
class ПремиумФункции {
static bool _имеетПремиум = false;
static bool get имеетПремиум => _имеетПремиум;
static Future<void> купитьПремиум() async {
// Интеграция с платежными системами
// Google Play Billing / App Store
_имеетПремиум = true;
}
static bool можетИспользовать(String функция) {
if (!_имеетПремиум) {
final бесплатныеФункции = [
'добавить_настроение',
'просмотр_истории_неделя',
'базовая_статистика',
];
return бесплатныеФункции.contains(функция);
}
return true; // Премиум пользователи могут все
}
}
2. Реклама
// Интеграция рекламы (AdMob)
import 'package:google_mobile_ads/google_mobile_ads.dart';
class РекламныйСервис {
static BannerAd? _баннернаяРеклама;
static InterstitialAd? _полноэкраннаяРеклама;
static Future<void> инициализировать() async {
await MobileAds.instance.initialize();
}
static void создатьБаннернуюРекламу() {
_баннернаяРеклама = BannerAd(
adUnitId: 'ca-app-pub-3940256099942544/6300978111', // Тестовый ID
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(),
);
_баннернаяРеклама!.load();
}
static Widget получитьБаннерВиджет() {
if (_баннернаяРеклама != null) {
return Container(
alignment: Alignment.center,
width: _баннернаяРеклама!.size.width.toDouble(),
height: _баннернаяРеклама!.size.height.toDouble(),
child: AdWidget(ad: _баннернаяРеклама!),
);
}
return SizedBox.shrink();
}
}
// Интеграция Firebase Analytics
import 'package:firebase_analytics/firebase_analytics.dart';
class АналитикаСервис {
static FirebaseAnalytics _аналитика = FirebaseAnalytics.instance;
static Future<void> отследитьСобытие(
String имяСобытия,
Map<String, dynamic>? параметры,
) async {
await _аналитика.logEvent(
name: имяСобытия,
parameters: параметры,
);
}
static Future<void> отследитьДобавлениеНастроения(String типНастроения) async {
await отследитьСобытие('добавлено_настроение', {
'тип_настроения': типНастроения,
'время_дня': DateTime.now().hour,
});
}
static Future<void> отследитьПросмотрСтатистики() async {
await отследитьСобытие('просмотр_статистики', null);
}
}
1. Native разработка
Android (Kotlin/Java)
iOS (Swift/Objective-C)
Глубокое понимание платформы
Максимальная производительность
2. Cross-platform разработка
Flutter (Dart)
React Native (JavaScript)
Xamarin (C#)
Одна кодовая база для всех платформ
3. Гибридная разработка
Ionic (HTML/CSS/JavaScript)
Cordova/PhoneGap
Веб-технологии в мобильном приложении
Технические навыки:
Знание архитектурных паттернов (MVP, MVVM, Clean Architecture)
Работа с базами данных (SQLite, Realm, Firebase)
Интеграция с REST API и GraphQL
Управление состоянием приложения
Тестирование (Unit, Widget, Integration тесты)
Дизайн и UX:
Понимание принципов Material Design и Human Interface Guidelines
Работа с Figma, Sketch
Знание принципов UI/UX дизайна
Адаптивность и accessibility
DevOps и развертывание:
CI/CD пайплайны
Автоматическое тестирование
Публикация в App Store и Google Play
Мониторинг и аналитика
Мобильная разработка открывает безграничные возможности для творчества и инноваций. От простых приложений-органайзеров до сложных социальных сетей и игр - мобильные технологии позволяют воплотить любые идеи в жизнь.
Ключевые принципы успешной мобильной разработки:
Пользователь в центре - всегда думайте о том, кто будет использовать ваше приложение
Простота и интуитивность - хороший интерфейс не нуждается в инструкции
Производительность - приложение должно работать быстро и плавно
Тестирование - проверяйте приложение на разных устройствах
Итеративность - выпускайте быстро, получайте обратную связь, улучшайте
Советы начинающим мобильным разработчикам:
Начните с простых проектов и постепенно усложняйте
Изучайте чужой код на GitHub
Участвуйте в хакатонах и конкурсах
Создавайте портфолио на GitHub
Следите за трендами в мобильной разработке
Не бойтесь экспериментировать с новыми технологиями
Помните: каждое популярное приложение начиналось с простой идеи и первой строчки кода. Instagram создавался как простое приложение для обмена фотографиями, TikTok начинался как Musical.ly, а WhatsApp - как альтернатива SMS.
Ваше приложение может стать следующим хитом! Начните разрабатывать уже сегодня! 📱✨