- Основная идея Flutter
- Минимальное Flutter-приложение
- StatelessWidget и StatefulWidget
- Взаимодействие с данными и setState()
- Provider, Riverpod, BLoC и др.
- Навигация и переход между экранами
- Полезные команды
- Советы по поддержке
- Кроссплатформенный фреймворк на языке Dart.
- Интерфейс рендерится движком Flutter напрямую (не через нативные элементы).
- Всё представлено в виде виджетов, аналог компонентов в React.
- Поддержка iOS, Android, Web, а также Desktop (Windows, macOS, Linux).
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Пример Flutter'),
),
body: Center(
child: Text('Всё – это виджет!'),
),
),
);
}
}
main()
— точка входа.runApp()
— запуск приложения.MaterialApp
— обёртка для темы, роутинга.Scaffold
— каркас экрана с AppBar и телом.
Не содержит внутреннего состояния, весь интерфейс зависит только от входных параметров.
import 'package:flutter/material.dart';
class HelloWidget extends StatelessWidget {
final String name;
const HelloWidget({Key? key, required this.name}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text('Привет, $name!', style: TextStyle(fontSize: 20));
}
}
Обладает собственным состоянием, обновляется при вызове setState()
.
import 'package:flutter/material.dart';
class CounterWidget extends StatefulWidget {
const CounterWidget({Key? key}) : super(key: key);
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Счётчик: $_count', style: TextStyle(fontSize: 24)),
ElevatedButton(
onPressed: _increment,
child: Text('Увеличить'),
),
],
);
}
}
Для простых случаев можно управлять состоянием напрямую через setState()
:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'State Management',
home: Scaffold(
appBar: AppBar(
title: Text('setState() пример'),
),
body: CounterWidget(),
),
);
}
}
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _incrementCounter() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_count', style: TextStyle(fontSize: 40)),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Увеличить'),
),
],
),
);
}
}
Взаимодействие с данными и state management
Используем ChangeNotifier
и ChangeNotifierProvider
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider пример')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (_, model, __) {
return Text('${model.count}', style: TextStyle(fontSize: 36));
},
),
ElevatedButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
child: Text('Увеличить'),
),
],
),
),
),
);
}
}
Используем глобальные провайдеры и ConsumerWidget
:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateProvider<int>((ref) => 0);
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Riverpod пример')),
body: Center(child: CounterWidget()),
),
);
}
}
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider.state).state;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count', style: TextStyle(fontSize: 36)),
ElevatedButton(
onPressed: () => ref.read(counterProvider.state).state++,
child: Text('Увеличить'),
),
],
);
}
}
Разделяем события (Event), состояния (State) и бизнес-логику (Bloc):
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// События
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// Состояния
class CounterState {
final int value;
CounterState(this.value);
}
// Логика
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<Increment>((event, emit) {
emit(CounterState(state.value + 1));
});
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final CounterBloc _counterBloc = CounterBloc();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (_) => _counterBloc,
child: Scaffold(
appBar: AppBar(title: Text('BLoC пример')),
body: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${state.value}', style: TextStyle(fontSize: 36)),
ElevatedButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Text('Увеличить'),
),
],
),
);
},
),
),
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
const FirstScreen({Key? key}) : super(key: key);
void _goToSecondScreen(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondScreen()),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Первый экран')),
body: Center(
child: ElevatedButton(
onPressed: () => _goToSecondScreen(context),
child: const Text('Перейти на второй экран'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({Key? key}) : super(key: key);
void _back(BuildContext context) {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Второй экран')),
body: Center(
child: ElevatedButton(
onPressed: () => _back(context),
child: const Text('Назад'),
),
),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Навигация с именованными маршрутами',
initialRoute: '/',
routes: {
'/': (context) => const FirstScreen(),
'/second': (context) => const SecondScreen(),
},
));
}
class FirstScreen extends StatelessWidget {
const FirstScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Первый экран')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: const Text('Перейти на второй экран'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Второй экран')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Назад'),
),
),
);
}
}
Более гибкая модель, используем декларативный подход (особенно для Web-приложений):
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _path = '/';
void _setPath(String newPath) {
setState(() {
_path = newPath;
});
}
List<Page> _buildPages() {
List<Page> pages = [
MaterialPage(
child: FirstScreen(onNavigate: () => _setPath('/second')),
),
];
if (_path == '/second') {
pages.add(MaterialPage(child: SecondScreen(onBack: () => _setPath('/'))));
}
return pages;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Navigator(
pages: _buildPages(),
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
_setPath('/');
return true;
},
),
);
}
}
class FirstScreen extends StatelessWidget {
final VoidCallback onNavigate;
const FirstScreen({Key? key, required this.onNavigate}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Первый экран')),
body: Center(
child: ElevatedButton(
onPressed: onNavigate,
child: const Text('Перейти на второй экран'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
final VoidCallback onBack;
const SecondScreen({Key? key, required this.onBack}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Второй экран')),
body: Center(
child: ElevatedButton(
onPressed: onBack,
child: const Text('Назад'),
),
),
);
}
}
# Проверка окружения (SDK, эмуляторы, IDE, плагины)
flutter doctor
# Установка/обновление зависимостей
flutter pub get
# Запуск приложения (c hot reload)
flutter run
# Сборка apk (Android) в релизном режиме
flutter build apk
# Сборка iOS в релизном режиме (только на macOS)
flutter build ios
# Запуск тестов
flutter test
# Просмотр устаревших версий пакетов
flutter pub outdated
# Обновление зависимостей до последних версий
flutter pub upgrade
-
Структура проекта
- Разделение кода в папке
lib/
(экраны, модели, сервисы). - Проверяйте
pubspec.yaml
для понимания используемых пакетов.
- Разделение кода в папке
-
Совместимость SDK и зависимостей
flutter doctor
иpubspec.lock
помогут выяснить версии.
-
State Management
- Определите, каким способом управляется состояние (Provider/BLoC и т.д.).
-
Тесты
- Используйте
flutter test
и обратите внимание на тестовые файлы вtest/
.
- Используйте
-
Обновление пакетов
- Не забывайте регулярно проверять
flutter pub outdated
.
- Не забывайте регулярно проверять
-
Анализ кода
dart analyze
или встроенные инструменты в IDE помогут в рефакторинге.