Flutter从入门到实战:手把手教你构建跨平台应用

Flutter作为Google推出的跨平台UI框架,凭借其高性能、热重载和丰富的组件库,已成为移动开发领域的热门选择。本文将从基础组件完整案例,结合图文与代码解析,帮助你快速掌握Flutter开发的核心技能。


一、Flutter核心优势:为什么选择它?

1. 跨平台开发

  • 一套代码跑多端:iOS、Android、Web、桌面应用(Windows/macOS/Linux)通用。
  • 原生性能:通过Dart语言编译为原生代码,避免WebView的卡顿问题。

2. 开发效率高

  • 热重载(Hot Reload):修改代码后立即生效,无需重新编译。
  • 丰富的组件库:内置Material Design和Cupertino(iOS风格)组件,开箱即用。

3. 社区与生态

  • GitHub星标数超150k:全球开发者活跃贡献,问题解决速度快。
  • 插件市场(pub.dev):提供大量第三方库(如网络请求、状态管理、动画等)。

二、基础组件详解:构建UI的基石

1. 文本与图标:Text & Icon

Text组件

dart

Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontSize: 24,
    color: Colors.blue,
    fontWeight: FontWeight.bold,
  ),
),

效果图
<img src="https://flutter.dev/assets/ui/widgets/text-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />
(注:实际使用时替换为本地截图)

关键属性

  • fontSize:字体大小。
  • color:文字颜色。
  • fontWeight:字体粗细(如boldnormal)。
Icon组件

dart

Icon(
  Icons.star,
  color: Colors.red,
  size: 30,
),

效果图
<img src="https://flutter.dev/assets/ui/widgets/icon-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />

常见图标库

  • Icons:Material Design图标集。
  • CupertinoIcons:iOS风格图标集。

2. 图片组件:Image

Flutter支持多种图片加载方式:

dart

// 网络图片
Image.network(
  'https://example.com/image.jpg',
  fit: BoxFit.cover, // 填充模式
  width: 200,
  height: 100,
),

// 本地图片(需在pubspec.yaml中配置assets)
Image.asset(
  'assets/images/logo.png',
  width: 100,
  height: 100,
),

效果图
<img src="https://flutter.dev/assets/ui/widgets/image-network-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />

关键属性

  • fit:控制图片填充方式(如covercontainfill)。
  • width/height:强制指定尺寸(可能导致变形)。

3. 布局组件:Column & Row

Flutter的布局基于Flex模型,核心组件为Column(垂直布局)和Row(水平布局)。

案例:构建一个标题栏

dart

Container(
  padding: EdgeInsets.all(16),
  child: Row(
    children: [
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Oeschinen Lake Campground', style: TextStyle(fontWeight: FontWeight.bold)),
            Text('Kandersteg, Switzerland', style: TextStyle(color: Colors.grey)),
          ],
        ),
      ),
      Icon(Icons.star, color: Colors.red),
      SizedBox(width: 8),
      Text('41'),
    ],
  ),
);

效果图
<img src="https://flutter.dev/assets/ui/layout/layout-row-column-expanded-demo-1c9a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />

关键点

  • Expanded:让子组件填充剩余空间。
  • crossAxisAlignment:控制子组件在交叉轴上的对齐方式(如左对齐、居中对齐)。

三、进阶组件:提升交互体验

1. 轮播图:PageView

轮播图是电商、新闻类App的常见需求,使用PageView可轻松实现:

dart

PageView(
  children: [
    Image.network('https://example.com/image1.jpg', fit: BoxFit.cover),
    Image.network('https://example.com/image2.jpg', fit: BoxFit.cover),
    Image.network('https://example.com/image3.jpg', fit: BoxFit.cover),
  ],
),

效果图
<img src="https://flutter.dev/assets/ui/widgets/pageview-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.gif" />

扩展功能

  • 添加指示器(如小圆点):通过PageControllerListView.builder实现。
  • 自动轮播:结合Timer定期切换页面。

2. 剪裁与特效:ClipOval & ClipRRect

Flutter支持多种剪裁方式,如圆形剪裁(ClipOval)和圆角矩形剪裁(ClipRRect):

dart

// 圆形剪裁
ClipOval(
  child: Image.network('https://example.com/avatar.jpg', width: 100, height: 100),
),

// 圆角矩形剪裁
ClipRRect(
  borderRadius: BorderRadius.circular(16),
  child: Image.network('https://example.com/banner.jpg', width: 200, height: 100),
),

效果图
<img src="https://flutter.dev/assets/ui/widgets/clip-oval-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />


四、实战案例:从0到1构建完整页面

案例1:详情页布局

需求:构建一个包含图片、标题、评分和按钮的详情页。
实现步骤

  1. 图片部分:使用Image.network加载网络图片。
  2. 标题部分:用RowColumn组合实现复杂布局。
  3. 按钮部分:封装可复用的按钮列。

完整代码

dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: DetailPage(),
    );
  }
}

class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('详情页')),
      body: ListView(
        children: [
          // 图片部分
          Image.network(
            'https://example.com/detail-image.jpg',
            height: 240,
            fit: BoxFit.cover,
          ),
          // 标题部分
          _buildTitleSection(),
          // 按钮部分
          _buildButtonSection(),
          // 描述部分
          _buildTextSection(),
        ],
      ),
    );
  }

  // 标题部分
  Widget _buildTitleSection() {
    return Container(
      padding: EdgeInsets.all(32),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Oeschinen Lake Campground',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                SizedBox(height: 8),
                Text(
                  'Kandersteg, Switzerland',
                  style: TextStyle(color: Colors.grey),
                ),
              ],
            ),
          ),
          FavoriteWidget(),
        ],
      ),
    );
  }

  // 按钮部分
  Widget _buildButtonSection() {
    return Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          _buildButtonColumn(Icons.call, 'CALL'),
          _buildButtonColumn(Icons.near_me, 'ROUTE'),
          _buildButtonColumn(Icons.share, 'SHARE'),
        ],
      ),
    );
  }

  Widget _buildButtonColumn(IconData icon, String label) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, color: Colors.blue),
        Container(
          margin: EdgeInsets.only(top: 8),
          child: Text(
            label,
            style: TextStyle(fontSize: 12),
          ),
        ),
      ],
    );
  }

  // 描述部分
  Widget _buildTextSection() {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 32),
      child: Text(
        'Oeschinen Lake is a must-visit destination for nature lovers and outdoor enthusiasts. Surrounded by the Bernese Alps, it offers breathtaking views and a wide range of activities.',
        softWrap: true, // 自动换行
      ),
    );
  }
}

// 评分组件
class FavoriteWidget extends StatefulWidget {
  @override
  _FavoriteWidgetState createState() => _FavoriteWidgetState();
}

class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  int _favoriteCount = 41;

  void _toggleFavorite() {
    setState(() {
      if (_isF) {
        _favoriteCount--;
      } else {
        _favoriteCount++;
      }
      _isFavorited = !_isFavorited;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        IconButton(
          icon: Icon(
            _isFavorited ? Icons.star : Icons.star_border,
            color: Colors.red,
          ),
          onPressed: _toggleFavorite,
        ),
        SizedBox(width: 4),
        Text('$_favoriteCount'),
      ],
    );
  }
}

效果图
<img src="https://flutter.dev/assets/ui/layout/layout-detail-page-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.png" />


案例2:网络请求与状态管理

需求:从API获取数据并显示在列表中,支持下拉刷新和加载更多。
实现步骤

  1. 网络请求:使用dio库发起HTTP请求。
  2. 状态管理:用Provider管理加载状态和列表数据。
  3. 下拉刷新:通过RefreshIndicator实现。
1. 添加依赖

pubspec.yaml中添加:

yaml

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.0.0
  provider: ^6.0.0
2. 网络请求工具类

dart

import 'package:dio/dio.dart';

class ApiService {
  static Future<List<Post>> fetchPosts() async {
    final response = await Dio().get('https://jsonplaceholder.typicode.com/posts');
    return List<Post>.from(response.data.map((item) => Post.fromJson(item)));
  }
}

class Post {
  final int id;
  final String title;
  final String body;

  Post({required this.id, required this.title, required this.body});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}
3. 状态管理(Provider)

dart

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

class PostProvider with ChangeNotifier {
  List<Post> _posts = [];
  bool _isLoading = false;

  List<Post> get posts => _posts;
  bool get isLoading => _isLoading;

  Future<void> fetchPosts() async {
    _isLoading = true;
    notifyListeners();
    try {
      final data = await ApiService.fetchPosts();
      _posts = data;
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}
4. 页面实现

dart

class PostListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final postProvider = Provider.of<PostProvider>(context);

    return Scaffold(
      appBar: AppBar(title: Text('文章列表')),
      body: RefreshIndicator(
        onRefresh: postProvider.fetchPosts,
        child: postProvider.isLoading
            ? Center(child: CircularProgressIndicator())
            : ListView.builder(
                itemCount: postProvider.posts.length,
                itemBuilder: (context, index) {
                  final post = postProvider.posts[index];
                  return ListTile(
                    title: Text(post.title),
                    subtitle: Text(post.body.substring(0, 50) + '...'),
                  );
                },
              ),
      ),
    );
  }
}
5. 主入口

dart

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => PostProvider()),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: PostListPage(),
      ),
    ),
  );
}

效果图
<img src="https://flutter.dev/assets/ui/widgets/refresh-indicator-demo-9f1a8e6f5e3b7f5f5a5b5e5a5b5e5a5b.gif" />


五、总结与学习资源推荐

1. 核心知识点总结

  • 布局:优先使用ColumnRow,复杂布局可结合ExpandedFlex
  • 图片处理Image组件支持多种加载方式,Clip系列组件可实现剪裁特效。
  • 状态管理:简单场景用setState,复杂场景推荐ProviderGetX
  • 网络请求dio库功能强大,支持拦截器和全局配置。

2. 学习资源推荐

  1. Flutter官方文档:最权威的学习资料。
  2. Flutter中文网:中文社区,适合初学者。
  3. GitHub示例库:开源项目参考。
  4. B站教程:视频教程更直观。

下期预告:《Flutter动画实战:让UI动起来》

Logo

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

更多推荐