在这里插入图片描述

这是一个完整的Flutter新闻应用开发系列教程。今天从项目初始化开始,搭建好基础框架,为后续开发做准备。

环境检查

首先确认Flutter环境是否正常:

flutter doctor

这个命令会检查Flutter SDK、Android工具链、iOS工具链、编辑器插件等。如果有问题会给出明确提示和解决方案。

检查要点

  • Flutter SDK是否安装
  • Android Studio是否配置
  • Xcode是否安装(macOS)
  • VS Code插件是否安装

创建项目

使用Flutter命令创建新项目:

flutter create flutter_application_1
cd flutter_application_1

项目结构说明

  • lib/ - Dart代码目录,所有业务代码都在这里
  • pubspec.yaml - 项目配置文件,管理依赖和资源
  • android/ - Android平台代码
  • ios/ - iOS平台代码
  • test/ - 测试代码目录

创建完成后,可以先运行一下看看默认的示例应用:

flutter run

配置项目依赖

打开pubspec.yaml,这是项目的配置文件。我们需要添加应用所需的依赖包:

name: flutter_application_1
description: 今日资讯 - Flutter跨平台新闻应用
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.4.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter

  # UI组件
  cupertino_icons: ^1.0.6
  
  # 网络请求
  http: ^1.1.0
  
  # 状态管理
  provider: ^6.1.1
  
  # 本地存储
  shared_preferences: ^2.2.2
  
  # 图片缓存
  cached_network_image: ^3.3.1
  shimmer: ^3.0.0
  flutter_staggered_grid_view: ^0.7.0
  
  # 工具类
  intl: ^0.19.0
  url_launcher: ^6.2.4
  share_plus: ^7.2.2
  pull_to_refresh: ^2.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

flutter:
  uses-material-design: true

依赖说明

  • http - 网络请求库,用于调用API获取新闻数据
  • provider - 状态管理方案,管理应用状态
  • shared_preferences - 本地键值对存储,保存用户设置
  • cached_network_image - 图片缓存库,提升图片加载速度
  • intl - 日期格式化和国际化支持
  • share_plus - 系统分享功能
  • url_launcher - 打开外部链接

安装依赖:

flutter pub get

这个命令会下载所有依赖包,可能需要几分钟时间。

规划目录结构

良好的目录结构是项目可维护性的基础。创建以下目录:

mkdir -p lib/models lib/services lib/providers lib/screens lib/widgets

目录职责划分

  • models/ - 数据模型,定义新闻、用户等数据结构
  • services/ - API调用和业务逻辑,封装网络请求
  • providers/ - 状态管理,使用Provider管理全局状态
  • screens/ - 页面,每个页面一个文件
  • widgets/ - 可复用组件,如新闻卡片、分类标签等

这种分层架构让代码职责清晰,便于团队协作和后期维护。

编写应用入口

创建lib/main.dart,这是应用的入口文件:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/theme_provider.dart';
import 'providers/news_provider.dart';
import 'providers/favorites_provider.dart';
import 'screens/splash_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => ThemeProvider()),
        ChangeNotifierProvider(create: (_) => NewsProvider()),
        ChangeNotifierProvider(create: (_) => FavoritesProvider()),
      ],
      child: Consumer<ThemeProvider>(
        builder: (context, themeProvider, child) {
          return MaterialApp(
            title: '今日资讯',
            debugShowCheckedModeBanner: false,
            theme: themeProvider.lightTheme,
            darkTheme: themeProvider.darkTheme,
            themeMode: themeProvider.themeMode,
            home: const SplashScreen(),
          );
        },
      ),
    );
  }
}

代码解析

  • MultiProvider - 注册多个Provider,让全局都能访问这些状态
  • ThemeProvider - 管理应用主题(深色/浅色模式)
  • NewsProvider - 管理新闻数据和加载状态
  • FavoritesProvider - 管理收藏数据
  • Consumer<ThemeProvider> - 监听主题变化,自动更新UI
  • debugShowCheckedModeBanner: false - 隐藏右上角的Debug标签
  • themeMode - 支持系统、浅色、深色三种主题模式

创建ThemeProvider

创建lib/providers/theme_provider.dart,管理应用主题:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.system;
  
  ThemeMode get themeMode => _themeMode;

  ThemeProvider() {
    _loadThemeMode();
  }

  Future<void> _loadThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    final themeModeString = prefs.getString('themeMode') ?? 'system';
    _themeMode = ThemeMode.values.firstWhere(
      (e) => e.toString() == 'ThemeMode.$themeModeString',
      orElse: () => ThemeMode.system,
    );
    notifyListeners();
  }

  Future<void> setThemeMode(ThemeMode mode) async {
    _themeMode = mode;
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('themeMode', mode.toString().split('.').last);
    notifyListeners();
  }

  ThemeData get lightTheme => ThemeData(
    useMaterial3: true,
    brightness: Brightness.light,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.light,
    ),
  );

  ThemeData get darkTheme => ThemeData(
    useMaterial3: true,
    brightness: Brightness.dark,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.dark,
    ),
  );
}

代码解析

  • ChangeNotifier - Provider的基类,提供状态通知功能
  • _loadThemeMode() - 从本地存储读取用户上次选择的主题
  • setThemeMode() - 切换主题并保存到本地,下次打开应用时恢复
  • notifyListeners() - 通知所有监听者更新UI
  • useMaterial3: true - 使用Material Design 3最新设计规范
  • ColorScheme.fromSeed - 从种子颜色生成完整配色方案,自动适配深浅色

创建NewsProvider

创建lib/providers/news_provider.dart,管理新闻数据:

import 'package:flutter/material.dart';
import '../models/news_article.dart';
import '../services/api_service.dart';

class NewsProvider extends ChangeNotifier {
  final ApiService _apiService = ApiService();
  final Map<String, List<NewsArticle>> _newsCache = {};
  bool _isLoading = false;
  String? _error;

  bool get isLoading => _isLoading;
  String? get error => _error;

  List<NewsArticle> getNewsByCategory(String category) {
    return _newsCache[category] ?? [];
  }

  Future<void> fetchNews(String category) async {
    if (_newsCache.containsKey(category) && _newsCache[category]!.isNotEmpty) {
      return;
    }

    _isLoading = true;
    _error = null;
    notifyListeners();

    try {
      List<NewsArticle> articles;
      switch (category) {
        case 'space':
          articles = await _apiService.fetchSpaceNews();
          break;
        case 'tech':
          articles = await _apiService.fetchTechNews();
          break;
        default:
          articles = await _apiService.fetchSpaceNews();
      }
      _newsCache[category] = articles;
    } catch (e) {
      _error = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  Future<void> refreshNews(String category) async {
    _newsCache.remove(category);
    await fetchNews(category);
  }
}

代码解析

  • _newsCache - 缓存不同分类的新闻数据,避免重复请求
  • _isLoading - 加载状态标志,用于显示加载动画
  • _error - 错误信息,用于显示错误提示
  • fetchNews() - 获取新闻,有缓存则直接返回,提升用户体验
  • refreshNews() - 清除缓存并重新获取,用于下拉刷新

这种缓存机制可以大大提升应用的响应速度,用户切换分类时几乎是瞬间完成的。

创建FavoritesProvider

创建lib/providers/favorites_provider.dart,管理收藏功能:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/news_article.dart';

class FavoritesProvider extends ChangeNotifier {
  List<NewsArticle> _favorites = [];
  
  List<NewsArticle> get favorites => _favorites;

  FavoritesProvider() {
    _loadFavorites();
  }

  Future<void> _loadFavorites() async {
    final prefs = await SharedPreferences.getInstance();
    final favoritesJson = prefs.getStringList('favorites') ?? [];
    _favorites = favoritesJson
        .map((json) => NewsArticle.fromJson(jsonDecode(json)))
        .toList();
    notifyListeners();
  }

  Future<void> _saveFavorites() async {
    final prefs = await SharedPreferences.getInstance();
    final favoritesJson = _favorites
        .map((article) => jsonEncode(article.toJson()))
        .toList();
    await prefs.setStringList('favorites', favoritesJson);
  }

  bool isFavorite(String articleId) {
    return _favorites.any((article) => article.id == articleId);
  }

  Future<void> toggleFavorite(NewsArticle article) async {
    if (isFavorite(article.id)) {
      _favorites.removeWhere((a) => a.id == article.id);
    } else {
      _favorites.insert(0, article);
    }
    await _saveFavorites();
    notifyListeners();
  }
}

代码解析

  • _loadFavorites() - 从本地存储加载收藏列表,应用启动时自动调用
  • _saveFavorites() - 保存收藏列表到本地,确保数据持久化
  • isFavorite() - 判断文章是否已收藏,用于显示收藏按钮状态
  • toggleFavorite() - 切换收藏状态,收藏或取消收藏
  • jsonEncode/jsonDecode - JSON序列化和反序列化,用于存储复杂对象

配置代码检查

创建analysis_options.yaml,配置代码检查规则:

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    prefer_const_constructors: true
    prefer_const_literals_to_create_immutables: true
    avoid_print: true

作用:使用Flutter官方推荐的lint规则,帮助发现代码问题,提升代码质量。

运行测试

现在可以尝试运行应用了:

flutter run

首次运行会编译整个应用,需要几分钟。后续运行会快很多,因为Flutter支持热重载。

注意:现在运行会报错,因为引用的文件还不存在(SplashScreen、NewsArticle等)。这是正常的,我们会在后续文章中逐步创建这些文件。

常见问题处理

依赖下载失败

如果在国内网络环境下,可能需要配置镜像:

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

版本冲突

如果遇到依赖版本冲突,可以使用dependency_overrides强制指定版本:

dependency_overrides:
  http: ^1.1.0

Gradle下载慢

编辑android/build.gradle,添加国内镜像:

repositories {
    maven { url 'https://maven.aliyun.com/repository/google' }
    maven { url 'https://maven.aliyun.com/repository/jcenter' }
    google()
    mavenCentral()
}

项目初始化完成

现在项目基础框架已经搭建完成,包括:

环境检查 - 确认Flutter环境正常
项目创建 - 创建Flutter项目
依赖配置 - 添加所需依赖包
目录结构 - 规划清晰的目录结构
应用入口 - 配置MultiProvider和主题
三个核心Provider - 主题、新闻、收藏
代码检查 - 配置lint规则

虽然引用的文件还不存在(会报错),但框架已经搭好,后续会逐步完善。这种先搭框架再填充内容的方式,可以让我们对整个应用的架构有清晰的认识。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里你可以找到更多Flutter开发资源,与其他开发者交流经验,共同进步。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐