Flutter — это мощный фреймворк для создания кроссплатформенных приложений, а WordPress — самая популярная система управления контентом. Их интеграция позволяет создать мобильное приложение для вашего WordPress-сайта с единой кодовой базой. В этой статье я подробно расскажу, как разработать такое приложение.
1. Подготовка WordPress
Перед разработкой приложения необходимо подготовить WordPress-сайт:
-
Установите REST API плагин (если не активирован по умолчанию):
-
WordPress уже имеет встроенный REST API, но для расширенных функций можно установить плагины типа «WP REST API» или «JWT Authentication for WP-API».
-
-
Настройка CORS:
-
Установите плагин «WP REST API — CORS» или добавьте в файл
.htaccess
:Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" Header set Access-Control-Allow-Headers "Content-Type, Authorization"
-
-
Создайте пользователя для API:
-
Создайте отдельного пользователя с правами редактора или администратора для доступа через API.
-
2. Настройка проекта Flutter
-
Создайте новый проект Flutter:
flutter create wordpress_app cd wordpress_app
-
Добавьте необходимые зависимости в
pubspec.yaml
:dependencies: flutter: sdk: flutter http: ^0.13.5 flutter_html: ^3.0.0-alpha.6 # для отображения HTML-контента cached_network_image: ^3.2.3 # для кэширования изображений provider: ^6.0.5 # для управления состоянием shared_preferences: ^2.1.1 # для локального хранения данных flutter_dotenv: ^5.0.2 # для хранения конфигурации
3. Основная архитектура приложения
Создайте базовую структуру проекта:
lib/ ├── models/ # Модели данных ├── services/ # Сервисы для работы с API ├── widgets/ # Кастомные виджеты ├── screens/ # Экраны приложения ├── utils/ # Вспомогательные утилиты └── main.dart # Точка входа
4. Работа с WordPress API
Создайте файл lib/services/wordpress_service.dart
:
import 'dart:convert'; import 'package:http/http.dart' as http; class WordPressService { final String baseUrl; WordPressService({required this.baseUrl}); Future<List<dynamic>> getPosts({int perPage = 10, int page = 1}) async { final response = await http.get( Uri.parse('$baseUrl/wp-json/wp/v2/posts?per_page=$perPage&page=$page'), ); if (response.statusCode == 200) { return json.decode(response.body); } else { throw Exception('Failed to load posts'); } } Future<dynamic> getPost(int id) async { final response = await http.get( Uri.parse('$baseUrl/wp-json/wp/v2/posts/$id'), ); if (response.statusCode == 200) { return json.decode(response.body); } else { throw Exception('Failed to load post'); } } Future<List<dynamic>> getCategories() async { final response = await http.get( Uri.parse('$baseUrl/wp-json/wp/v2/categories'), ); if (response.statusCode == 200) { return json.decode(response.body); } else { throw Exception('Failed to load categories'); } } }
5. Создание моделей данных
Пример модели для поста (lib/models/post_model.dart
):
class Post { final int id; final String title; final String content; final String excerpt; final String featuredImageUrl; final String date; final String author; final List<int> categories; Post({ required this.id, required this.title, required this.content, required this.excerpt, required this.featuredImageUrl, required this.date, required this.author, required this.categories, }); factory Post.fromJson(Map<String, dynamic> json) { return Post( id: json['id'], title: json['title']['rendered'], content: json['content']['rendered'], excerpt: json['excerpt']['rendered'], featuredImageUrl: json['featured_media'] != 0 ? json['_links']['wp:featuredmedia'][0]['href'] : '', date: json['date'], author: json['_links']['author'][0]['href'], categories: List<int>.from(json['categories']), ); } }
6. Создание основных экранов
Пример экрана со списком постов (lib/screens/posts_screen.dart
):
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/post_model.dart'; import '../services/wordpress_service.dart'; class PostsScreen extends StatefulWidget { _PostsScreenState createState() => _PostsScreenState(); } class _PostsScreenState extends State<PostsScreen> { late Future<List<Post>> futurePosts; final ScrollController _scrollController = ScrollController(); int _page = 1; List<Post> _posts = []; bool _isLoading = false; void initState() { super.initState(); futurePosts = _fetchPosts(); _scrollController.addListener(_scrollListener); } Future<List<Post>> _fetchPosts() async { final wordpressService = Provider.of<WordPressService>(context, listen: false); final data = await wordpressService.getPosts(page: _page); return data.map((json) => Post.fromJson(json)).toList(); } void _scrollListener() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _loadMorePosts(); } } Future<void> _loadMorePosts() async { if (_isLoading) return; setState(() { _isLoading = true; _page++; }); final newPosts = await _fetchPosts(); setState(() { _posts.addAll(newPosts); _isLoading = false; }); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Последние статьи'), ), body: FutureBuilder<List<Post>>( future: futurePosts, builder: (context, snapshot) { if (snapshot.hasData) { _posts = snapshot.data!; return ListView.builder( controller: _scrollController, itemCount: _posts.length + (_isLoading ? 1 : 0), itemBuilder: (context, index) { if (index == _posts.length) { return Center(child: CircularProgressIndicator()); } final post = _posts[index]; return ListTile( title: Text(post.title), subtitle: Text(post.excerpt), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => PostDetailScreen(post: post), ), ); }, ); }, ); } else if (snapshot.hasError) { return Center(child: Text("Ошибка загрузки данных")); } return Center(child: CircularProgressIndicator()); }, ), ); } void dispose() { _scrollController.dispose(); super.dispose(); } }
7. Интеграция аутентификации
Для работы с защищенными endpoint’ами WordPress API:
-
Установите JWT плагин на WordPress:
-
«JWT Authentication for WP-API»
-
-
Добавьте методы аутентификации в WordPressService:
Future<String> login(String username, String password) async { final response = await http.post( Uri.parse('$baseUrl/wp-json/jwt-auth/v1/token'), body: { 'username': username, 'password': password, }, ); if (response.statusCode == 200) { final data = json.decode(response.body); return data['token']; } else { throw Exception('Login failed'); } } Future<dynamic> createPost(Map<String, dynamic> data, String token) async { final response = await http.post( Uri.parse('$baseUrl/wp-json/wp/v2/posts'), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: json.encode(data), ); if (response.statusCode == 201) { return json.decode(response.body); } else { throw Exception('Failed to create post'); } }
8. Оптимизация производительности
-
Кэширование данных:
-
Используйте
shared_preferences
илиhive
для локального хранения данных -
Реализуйте стратегию «stale-while-revalidate»
-
-
Оптимизация изображений:
-
Используйте
cached_network_image
для кэширования изображений -
Реализуйте lazy loading для изображений
-
-
Пагинация и бесконечная прокрутка:
-
Как показано в примере PostsScreen
-
9. Публикация приложения
-
Настройте окружение:
flutter pub get flutter pub run flutter_dotenv:generate
-
Создайте файл .env:
WORDPRESS_URL=https://your-site.com
-
Запустите приложение:
flutter run
-
Соберите релизные версии:
flutter build apk --release # для Android flutter build ios --release # для iOS
Заключение
Разработка приложения для WordPress на Flutter позволяет создать производительное кроссплатформенное решение с единой кодовой базой. Основные преимущества:
-
Доступ к полному функционалу WordPress через REST API
-
Высокая производительность и нативный пользовательский интерфейс
-
Возможность работы оффлайн с кэшированием данных
-
Единая кодовая база для iOS и Android
Для дальнейшего развития приложения можно добавить:
-
Push-уведомления через Firebase
-
Комментарии и формы
-
Поиск по контенту
-
Темную тему и другие настройки пользователя
Flutter предоставляет все необходимые инструменты для создания полноценного мобильного клиента вашего WordPress-сайта. Но все же это рекомендации! Изучайте документацию к языкам.