Flutter艺术探索-Flutter常用UI组件:Text、Image、Button详解

Flutter常用UI组件深度解析:Text、Image、Button详解

引言

在Flutter应用开发中,UI组件是构建用户界面的基石。作为一款跨平台的UI框架,Flutter提供了丰富而强大的组件库,其中Text、Image和Button是使用频率最高、最基础的核心组件。理解这些组件的内部机制,掌握其高级用法,能够显著提升开发效率和应用程序质量。本文将从技术原理、实现机制、最佳实践和性能优化等多个维度,深入剖析这三个关键组件,为Flutter开发者提供全面而实用的指导。

技术分析

1. Text组件的核心机制

1.1 文本渲染原理

Flutter的Text组件基于dart:ui库中的Paragraph类实现,底层通过Skia图形库进行文本渲染。文本布局过程分为以下步骤:

dart 复制代码
// 文本布局流程示例
void _layoutText() {
  // 1. 创建段落构建器
  final builder = ui.ParagraphBuilder(ui.ParagraphStyle(
    textAlign: TextAlign.start,
    maxLines: 1,
  ));
  
  // 2. 添加文本样式
  builder.pushStyle(ui.TextStyle(
    color: ui.Color(0xFF000000),
    fontSize: 14.0,
  ));
  
  // 3. 添加文本内容
  builder.addText('Hello Flutter');
  
  // 4. 构建段落
  final paragraph = builder.build();
  
  // 5. 布局计算
  paragraph.layout(ui.ParagraphConstraints(width: 200));
  
  // 6. 绘制
  canvas.drawParagraph(paragraph, Offset.zero);
}
1.2 样式继承与覆盖

Flutter的文本样式支持继承机制,通过DefaultTextStyle实现样式传递:

dart 复制代码
class TextInheritanceExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: TextStyle(
        fontSize: 16,
        color: Colors.black,
        fontWeight: FontWeight.normal,
      ),
      child: Column(
        children: [
          Text('继承默认样式'),
          Text(
            '覆盖部分样式',
            style: TextStyle(
              fontWeight: FontWeight.bold, // 只覆盖加粗
              color: Colors.blue, // 覆盖颜色
            ),
          ),
          DefaultTextStyle.merge(
            style: TextStyle(fontStyle: FontStyle.italic),
            child: Text('合并新样式'),
          ),
        ],
      ),
    );
  }
}

2. Image组件的加载与渲染

2.1 图片加载流程

Flutter的Image组件采用四级缓存策略:

  1. 内存缓存(ImageCache)
  2. 资源缓存(AssetBundle)
  3. 网络缓存(HTTP缓存)
  4. 文件系统缓存
dart 复制代码
// 图片加载状态管理
class _ImageLoadState extends State<ImageLoadExample> {
  late ImageStream _imageStream;
  late ImageStreamListener _imageListener;
  
  @override
  void initState() {
    super.initState();
    _loadImage();
  }
  
  void _loadImage() {
    final ImageProvider provider = NetworkImage(
      'https://example.com/image.jpg',
      headers: {'Authorization': 'Bearer token'},
    );
    
    final config = ImageConfiguration(
      bundle: DefaultAssetBundle.of(context),
      devicePixelRatio: MediaQuery.of(context).devicePixelRatio,
    );
    
    _imageStream = provider.resolve(config);
    _imageListener = ImageStreamListener(
      (image, synchronousCall) {
        // 图片加载完成
        print('图片尺寸: ${image.image.width}×${image.image.height}');
      },
      onChunk: (chunk) {
        // 加载进度
        print('加载进度: ${chunk.cumulativeBytesLoaded}/${chunk.expectedTotalBytes}');
      },
      onError: (error, stackTrace) {
        // 加载错误处理
        print('图片加载失败: $error');
      },
    );
    
    _imageStream.addListener(_imageListener);
  }
}
2.2 图片格式与优化

Flutter支持多种图片格式,每种格式有不同的适用场景:

格式 特点 适用场景
PNG 无损压缩,支持透明 图标、UI元素
JPEG 有损压缩,文件小 照片、复杂图像
WebP 谷歌开发,压缩率高 网络传输
GIF 支持动画 简单动画
SVG 矢量格式 图标、可缩放图形

3. Button组件的状态管理

3.1 按钮类型与特点

Flutter提供了多种按钮组件,各有不同的使用场景:

dart 复制代码
// 按钮类型对比示例
class ButtonComparison extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 1. ElevatedButton - 主要操作
        ElevatedButton(
          onPressed: () => _handleAction('Elevated'),
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue,
            foregroundColor: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          child: Text('主要操作'),
        ),
        
        SizedBox(height: 10),
        
        // 2. OutlinedButton - 次要操作
        OutlinedButton(
          onPressed: () => _handleAction('Outlined'),
          style: OutlinedButton.styleFrom(
            side: BorderSide(color: Colors.blue, width: 2),
            padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
          ),
          child: Text('次要操作'),
        ),
        
        SizedBox(height: 10),
        
        // 3. TextButton - 文本操作
        TextButton(
          onPressed: () => _handleAction('Text'),
          style: TextButton.styleFrom(
            foregroundColor: Colors.blue,
            padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
          ),
          child: Text('文本操作'),
        ),
        
        SizedBox(height: 10),
        
        // 4. IconButton - 图标操作
        IconButton(
          onPressed: () => _handleAction('Icon'),
          icon: Icon(Icons.favorite),
          color: Colors.red,
          tooltip: '收藏',
        ),
      ],
    );
  }
  
  void _handleAction(String type) {
    print('按钮点击: $type');
  }
}
3.2 按钮状态管理

按钮的状态管理涉及多种交互状态:

dart 复制代码
class StatefulButtonExample extends StatefulWidget {
  @override
  _StatefulButtonExampleState createState() => _StatefulButtonExampleState();
}

class _StatefulButtonExampleState extends State<StatefulButtonExample> {
  bool _isLoading = false;
  bool _isDisabled = false;
  
  Future<void> _simulateAsyncOperation() async {
    setState(() => _isLoading = true);
    
    try {
      await Future.delayed(Duration(seconds: 2));
      print('操作完成');
    } catch (e) {
      print('操作失败: $e');
    } finally {
      if (mounted) {
        setState(() => _isLoading = false);
      }
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 加载状态按钮
        ElevatedButton(
          onPressed: _isLoading || _isDisabled ? null : _simulateAsyncOperation,
          child: _isLoading 
              ? SizedBox(
                  width: 20,
                  height: 20,
                  child: CircularProgressIndicator(
                    strokeWidth: 2,
                    valueColor: AlwaysStoppedAnimation(Colors.white),
                  ),
                )
              : Text('提交'),
        ),
        
        SizedBox(height: 20),
        
        // 开关控制按钮状态
        SwitchListTile(
          title: Text('禁用按钮'),
          value: _isDisabled,
          onChanged: (value) {
            setState(() => _isDisabled = value);
          },
        ),
        
        // 自定义状态管理
        _CustomStateButton(
          onPressed: _handleCustomButton,
        ),
      ],
    );
  }
}

代码实现

完整可运行的示例应用

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter UI组件详解',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const UIComponentsHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class UIComponentsHomePage extends StatefulWidget {
  const UIComponentsHomePage({super.key});

  @override
  State<UIComponentsHomePage> createState() => _UIComponentsHomePageState();
}

class _UIComponentsHomePageState extends State<UIComponentsHomePage> {
  int _currentIndex = 0;
  
  final List<Widget> _pages = [
    TextComponentDemo(),
    ImageComponentDemo(),
    ButtonComponentDemo(),
  ];
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter UI组件详解'),
        centerTitle: true,
      ),
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) {
          setState(() => _currentIndex = index);
        },
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.text_fields),
            label: 'Text组件',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.image),
            label: 'Image组件',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.touch_app),
            label: 'Button组件',
          ),
        ],
      ),
    );
  }
}

// Text组件演示页面
class TextComponentDemo extends StatefulWidget {
  @override
  _TextComponentDemoState createState() => _TextComponentDemoState();
}

class _TextComponentDemoState extends State<TextComponentDemo> {
  String _inputText = 'Flutter UI开发';
  double _fontSize = 16.0;
  Color _textColor = Colors.black;
  
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 1. 基础文本
          _buildSectionTitle('基础文本示例'),
          Text(
            '这是一个普通的文本',
            style: TextStyle(fontSize: _fontSize),
          ),
          
          const SizedBox(height: 20),
          
          // 2. 富文本
          _buildSectionTitle('富文本示例'),
          RichText(
            text: TextSpan(
              style: DefaultTextStyle.of(context).style,
              children: [
                TextSpan(
                  text: '富文本',
                  style: TextStyle(
                    color: Colors.blue,
                    fontWeight: FontWeight.bold,
                    fontSize: 18,
                  ),
                ),
                const TextSpan(text: '允许在同一文本中使用'),
                TextSpan(
                  text: '多种样式',
                  style: TextStyle(
                    color: Colors.red,
                    fontStyle: FontStyle.italic,
                    decoration: TextDecoration.underline,
                  ),
                ),
              ],
            ),
          ),
          
          const SizedBox(height: 20),
          
          // 3. 文本样式控制
          _buildSectionTitle('文本样式控制'),
          Text(
            _inputText,
            style: TextStyle(
              fontSize: _fontSize,
              color: _textColor,
              fontWeight: FontWeight.w600,
            ),
            textAlign: TextAlign.center,
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
          
          // 控制面板
          _buildControlPanel(),
          
          // 4. 国际化文本
          _buildSectionTitle('国际化支持'),
          const Text.rich(
            TextSpan(
              children: [
                TextSpan(text: '当前Locale: '),
                TextSpan(
                  text: 'zh_CN',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
  
  Widget _buildSectionTitle(String title) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10),
      child: Text(
        title,
        style: const TextStyle(
          fontSize: 18,
          fontWeight: FontWeight.bold,
          color: Colors.blue,
        ),
      ),
    );
  }
  
  Widget _buildControlPanel() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            // 文本输入
            TextField(
              decoration: const InputDecoration(
                labelText: '输入文本内容',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                setState(() => _inputText = value);
              },
            ),
            
            const SizedBox(height: 16),
            
            // 字体大小控制
            Row(
              children: [
                const Text('字体大小: '),
                Expanded(
                  child: Slider(
                    value: _fontSize,
                    min: 12,
                    max: 36,
                    divisions: 12,
                    label: _fontSize.toStringAsFixed(1),
                    onChanged: (value) {
                      setState(() => _fontSize = value);
                    },
                  ),
                ),
              ],
            ),
            
            // 颜色选择
            Wrap(
              spacing: 8,
              children: [
                _buildColorOption(Colors.black, '黑色'),
                _buildColorOption(Colors.blue, '蓝色'),
                _buildColorOption(Colors.red, '红色'),
                _buildColorOption(Colors.green, '绿色'),
              ],
            ),
          ],
        ),
      ),
    );
  }
  
  Widget _buildColorOption(Color color, String label) {
    return ChoiceChip(
      label: Text(label),
      selected: _textColor == color,
      selectedColor: color.withOpacity(0.3),
      onSelected: (selected) {
        setState(() => _textColor = color);
      },
    );
  }
}

// Image组件演示页面
class ImageComponentDemo extends StatefulWidget {
  @override
  _ImageComponentDemoState createState() => _ImageComponentDemoState();
}

class _ImageComponentDemoState extends State<ImageComponentDemo> {
  ImageProvider? _selectedImage;
  bool _isLoading = false;
  double _imageOpacity = 1.0;
  
  final List<String> _imageUrls = [
    'https://picsum.photos/300/200?random=1',
    'https://picsum.photos/300/200?random=2',
    'https://picsum.photos/300/200?random=3',
  ];
  
  @override
  void initState() {
    super.initState();
    _selectedImage = NetworkImage(_imageUrls[0]);
  }
  
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        children: [
          // 图片显示区域
          Card(
            elevation: 4,
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                children: [
                  if (_selectedImage != null)
                    AnimatedOpacity(
                      opacity: _imageOpacity,
                      duration: const Duration(milliseconds: 300),
                      child: Image(
                        image: _selectedImage!,
                        width: 300,
                        height: 200,
                        fit: BoxFit.cover,
                        loadingBuilder: (context, child, loadingProgress) {
                          if (loadingProgress == null) return child;
                          return Center(
                            child: CircularProgressIndicator(
                              value: loadingProgress.expectedTotalBytes != null
                                  ? loadingProgress.cumulativeBytesLoaded /
                                      loadingProgress.expectedTotalBytes!
                                  : null,
                            ),
                          );
                        },
                        errorBuilder: (context, error, stackTrace) {
                          return Container(
                            width: 300,
                            height: 200,
                            color: Colors.grey[200],
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                const Icon(
                                  Icons.error_outline,
                                  color: Colors.red,
                                  size: 48,
                                ),
                                const SizedBox(height: 8),
                                Text(
                                  '图片加载失败',
                                  style: TextStyle(color: Colors.red),
                                ),
                                Text(
                                  error.toString(),
                                  style: TextStyle(
                                    color: Colors.grey,
                                    fontSize: 12,
                                  ),
                                  maxLines: 2,
                                ),
                              ],
                            ),
                          );
                        },
                      ),
                    ),
                  
                  const SizedBox(height: 16),
                  
                  // 图片控制
                  _buildImageControls(),
                ],
              ),
            ),
          ),
          
          const SizedBox(height: 20),
          
          // 图片选择
          _buildImageSelection(),
          
          // 本地图片示例
          _buildLocalImageExample(),
        ],
      ),
    );
  }
  
  Widget _buildImageControls() {
    return Column(
      children: [
        // 透明度控制
        Row(
          children: [
            const Text('透明度: '),
            Expanded(
              child: Slider(
                value: _imageOpacity,
                min: 0.1,
                max: 1.0,
                onChanged: (value) {
                  setState(() => _imageOpacity = value);
                },
              ),
            ),
          ],
        ),
        
        // 加载控制
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ElevatedButton(
              onPressed: _isLoading ? null : _reloadImage,
              child: const Text('重新加载'),
            ),
            ElevatedButton(
              onPressed: _clearCache,
              child: const Text('清除缓存'),
            ),
          ],
        ),
      ],
    );
  }
  
  Widget _buildImageSelection() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              '选择网络图片',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Wrap(
              spacing: 10,
              children: _imageUrls.map((url) {
                return GestureDetector(
                  onTap: () {
                    setState(() {
                      _selectedImage = NetworkImage(url);
                      _imageOpacity = 1.0;
                    });
                  },
                  child: Container(
                    width: 80,
                    height: 60,
                    decoration: BoxDecoration(
                      border: Border.all(
                        color: _selectedImage is NetworkImage &&
                                (_selectedImage as NetworkImage).url == url
                            ? Colors.blue
                            : Colors.transparent,
                        width: 2,
                      ),
                      borderRadius: BorderRadius.circular(8),
                    ),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(6),
                      child: Image.network(
                        url,
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }
  
  Widget _buildLocalImageExample() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              '本地图片示例',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                // Asset图片
                Column(
                  children: [
                    Image.asset(
                      'assets/images/sample.png',
                      width: 100,
                      height: 100,
                      errorBuilder: (context, error, stackTrace) {
                        return Container(
                          width: 100,
                          height: 100,
                          color: Colors.grey[200],
                          child: const Center(
                            child: Icon(Icons.image_not_supported),
                          ),
                        );
                      },
                    ),
                    const SizedBox(height: 8),
                    const Text('Asset图片'),
                  ],
                ),
                
                // Memory图片
                Column(
                  children: [
                    FutureBuilder<Uint8List>(
                      future: _loadSampleImage(),
                      builder: (context, snapshot) {
                        if (snapshot.hasData) {
                          return Image.memory(
                            snapshot.data!,
                            width: 100,
                            height: 100,
                          );
                        }
                        return Container(
                          width: 100,
                          height: 100,
                          color: Colors.grey[200],
                          child: const Center(
                            child: CircularProgressIndicator(),
                          ),
                        );
                      },
                    ),
                    const SizedBox(height: 8),
                    const Text('Memory图片'),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
  
  Future<Uint8List> _loadSampleImage() async {
    final byteData = await rootBundle.load('assets/images/sample.png');
    return byteData.buffer.asUint8List();
  }
  
  void _reloadImage() async {
    if (_selectedImage is NetworkImage) {
      setState(() => _isLoading = true);
      await Future.delayed(const Duration(seconds: 1));
      // 清除缓存并重新加载
      _selectedImage!.evict();
      setState(() {
        _isLoading = false;
        _imageOpacity = 1.0;
      });
    }
  }
  
  void _clearCache() {
    imageCache.clear();
    imageCache.clearLiveImages();
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('图片缓存已清除')),
    );
  }
}

// Button组件演示页面
class ButtonComponentDemo extends StatefulWidget {
  @override
  _ButtonComponentDemoState createState() => _ButtonComponentDemoState();
}

class _ButtonComponentDemoState extends State<ButtonComponentDemo> {
  int _counter = 0;
  bool _toggleState = false;
  
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        children: [
          // 计数器展示
          Card(
            child: Padding(
              padding: const EdgeInsets.all(20),
              child: Column(
                children: [
                  const Text(
                    '按钮交互计数器',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 20),
                  Text(
                    '$_counter',
                    style: const TextStyle(
                      fontSize: 48,
                      fontWeight: FontWeight.bold,
                      color: Colors.blue,
                    ),
                  ),
                  const SizedBox(height: 20),
                  const Text('点击下方按钮增加计数值'),
                ],
              ),
            ),
          ),
          
          const SizedBox(height: 20),
          
          // 基础按钮示例
          _buildButtonSection(),
          
          const SizedBox(height: 20),
          
          // 按钮状态管理
          _buildStatefulButtonSection(),
          
          const SizedBox(height: 20),
          
          // 自定义按钮
          _buildCustomButtonSection(),
        ],
      ),
    );
  }
  
  Widget _buildButtonSection() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              '基础按钮类型',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 10),
            
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                // ElevatedButton
                ElevatedButton(
                  onPressed: () => _incrementCounter('Elevated'),
                  child: const Text('Elevated'),
                ),
                
                // OutlinedButton
                OutlinedButton(
                  onPressed: () => _incrementCounter('Outlined'),
                  child: const Text('Outlined'),
                ),
                
                // TextButton
                TextButton(
                  onPressed: () => _incrementCounter('Text'),
                  child: const Text('Text'),
                ),
                
                // IconButton
                IconButton(
                  onPressed: () => _incrementCounter('Icon'),
                  icon: const Icon(Icons.add),
                  tooltip: '增加',
                ),
                
                // FloatingActionButton
                FloatingActionButton.small(
                  onPressed: () => _incrementCounter('FAB'),
                  child: const Icon(Icons.plus_one),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
  
  Widget _buildStatefulButtonSection() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              '按钮状态管理',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 10),
            
            // 禁用状态
            Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    ElevatedButton(
                      onPressed: null,
                      child: const Text('禁用按钮'),
                    ),
                    OutlinedButton(
                      onPressed: null,
                      child: const Text('禁用按钮'),
                    ),
                  ],
                ),
                const SizedBox(height: 10),
                const Text(
                  '按钮被禁用时的外观',
                  style: TextStyle(color: Colors.grey),
                ),
              ],
            ),
            
            const SizedBox(height: 20),
            
            // 加载状态
            _buildLoadingButton(),
            
            const SizedBox(height: 20),
            
            // 开关状态
            SwitchListTile(
             
相关推荐
kirk_wang41 分钟前
Flutter艺术探索-Flutter路由导航基础:Navigator使用详解
flutter·移动开发·flutter教程·移动开发教程
行者961 小时前
Flutter跨平台开发:OpenHarmony平台卡片翻转组件的优化实践
flutter·harmonyos·鸿蒙
kirk_wang1 小时前
Flutter艺术探索-Flutter布局基础:Row、Column、Container实战
flutter·移动开发·flutter教程·移动开发教程
kirk_wang1 小时前
Flutter share_plus 库鸿蒙端适配实践:打通跨平台分享功能
flutter·移动开发·跨平台·arkts·鸿蒙
行者961 小时前
Flutter适配OpenHarmony:手势交互的深度优化与实战应用
flutter·交互·harmonyos·鸿蒙
行者961 小时前
Flutter与OpenHarmony深度融合:跨平台日历组件性能优化与适配实践
flutter·harmonyos·鸿蒙
行者962 小时前
Flutter适配鸿蒙:SnackBar组件实践与优化策略
flutter·harmonyos·鸿蒙
kirk_wang2 小时前
Flutter艺术探索-ListView与GridView列表组件完全指南
flutter·移动开发·flutter教程·移动开发教程
消失的旧时光-194313 小时前
Flutter 插件通信架构设计:从 Channel 到 FFI 的完整边界
flutter·ffi
郑梓斌16 小时前
Luban 2 Flutter:一行代码在 Flutter 开发中实现图片压缩功能
flutter·ios