Flutter实战:构建类似ChatGPT的聊天应用(四)

Flutter AI 访问多个AI 模型

在上一篇中我们构建了一个可以访问多模型聊天应用,现在设置菜单可以选择不同的大模型

因为代码较多,没有将所有代码贴出(也没有上传GitHub)如果有需要在在评论区留言,我会更新文章或者上传代码

模型选择

我创建了一个名为 _showPopupMenu 的函数,在用户点击 IconButton 时调用该函数,然后使用 showMenu 函数显示一个类似于 PopupMenuButton 的菜单。用户选择了某个 AI 类型后会更新标题和相应的 AI 服务实例。

我增加一个 _showAISelectionMenu 方法

写法 1

  void _showAISelectionMenu(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return Container(
          child: Wrap(
            children: <Widget>[
              ListTile(
                leading: Icon(Icons.chat),
                title: Text('ChatGPT'),
                onTap: () {
                  setState(() {
                    _appBarTitle = 'ChatGPT'; // 更新标题
                    _aiProvider = AIProvider(type: 'ChatGPT'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              ListTile(
                leading: Icon(Icons.android),
                title: Text('文心一言'),
                onTap: () {
                  setState(() {
                    _appBarTitle = '文心一言'; // 更新标题
                    _aiProvider = AIProvider(type: 'ErnieBot'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              ListTile(
                leading: Icon(Icons.question_answer),
                title: Text('通义千问'),
                onTap: () {
                  setState(() {
                    _appBarTitle = '通义千问'; // 更新标题
                    _aiProvider = AIProvider(type: 'QwenBot'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              // 添加更多的 AI 类型
            ],
          ),
        );
      },
    );
  }
  
  //...
}

这样写方法中重复代码太多了,我将 AI 类型和对应的标题定义为两个列表 aiTypes 和 aiTitles。然后在 _showAISelectionMenu 方法中使用 ListView.builder 来遍历这两个列表,生成对应的 ListTile 列表。同时,我添加了一个辅助方法 _buildIcon 来根据 AI 类型生成对应的图标。这样可以减少重复的代码,并使代码更加清晰易读。

写法 2

void _showAISelectionMenu(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Container(
        child: ListView.builder(
          itemCount: aiTypes.length,
          itemBuilder: (BuildContext context, int index) {
            String aiType = aiTypes[index];
            String aiTitle = aiTitles[index];
            return ListTile(
              leading: _buildIcon(aiType),
              title: Text(aiTitle),
              onTap: () {
                setState(() {
                  _appBarTitle = aiTitle; // 更新标题
                  _aiProvider = AIProvider(type: aiType); // 更新 AIProvider
                });
                Navigator.pop(context);
              },
            );
          },
        ),
      );
    },
  );
}

Widget _buildIcon(String aiType) {
  switch (aiType) {
    case 'ChatGPT':
      return Icon(Icons.chat);
    case 'ErnieBot':
      return Icon(Icons.android);
    case 'QwenBot':
      return Icon(Icons.question_answer);
    default:
      return Icon(Icons.help);
  }
}

final List<String> aiTypes = ['ChatGPT', 'ErnieBot', 'QwenBot'];
final List<String> aiTitles = ['ChatGPT', '文心一言', '通义千问'];

具体模型选择

写法 1

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _textController = TextEditingController();
  final List<Message> _messages = [];

  AIProvider _aiProvider = AIProvider();
  String _appBarTitle = 'ChatGPT'; // 初始标题

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_appBarTitle),
        actions: [
          IconButton(
            icon: Icon(Icons.settings),
            onPressed: () {
              _showPopupMenu(context);
            },
          ),
        ],
      ),
      body: Container(
        //...
      ),
    );
  }

  void _showPopupMenu(BuildContext context) async {
    final selectedValue = await showMenu<String>(
      context: context,
      position: RelativeRect.fromLTRB(100, 100, 0, 100), // 根据需要调整位置
      items: [
        PopupMenuItem<String>(
          value: 'ChatGPT',
          child: Text('ChatGPT'),
        ),
        PopupMenuItem<String>(
          value: 'ErnieBot',
          child: Text('文心一言'),
        ),
        PopupMenuItem<String>(
          value: 'QwenBot',
          child: Text('通义千问'),
        ),
        // 添加更多的 AI 类型
      ],
    );

    if (selectedValue != null) {
      setState(() {
        _appBarTitle = selectedValue == 'QwenBot' ? '通义千问' : selectedValue; // 更新标题
        _aiProvider = AIProvider(type: selectedValue); // 更新 AIProvider
      });
    }
  }

  //...
}

这样写方法中重复代码太多了,我们可以使用 List.generate 来生成 PopupMenuButton 的子菜单项,通过循环遍历 aiTypes 列表,并使用每个元素创建一个 PopupMenuItem。这样就可以实现通过循环构造 PopupMenuButton 的子菜单项。

写法 2

void _showPopupMenu(BuildContext context) {
  showMenu(
    context: context,
    position: RelativeRect.fromLTRB(0, AppBar().preferredSize.height, 0, 0),
    items: List.generate(aiTypes.length, (index) {
      return PopupMenuItem<String>(
        value: aiTypes[index],
        child: Text(aiTitles[index]),
      );
    }),
    elevation: 8.0,
  ).then((value) {
    if (value != null) {
      setState(() {
        _appBarTitle = value == 'QwenBot' ? '通义千问' : value; // 更新标题
        _aiProvider = AIProvider(type: value); // 更新 AIProvider
      });
    }
  });
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容