You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何为GridView添加onTap实现跳转至指定页面

为Flutter GridView中的ServiceBox添加点击跳转功能

我已完成包含GridView组件的首页HomePage开发,通过ServiceBox组件构建网格项,现需要为每个网格容器添加onTap点击事件,实现点击后跳转至对应专属页面的功能。

原代码

HomePage代码

import 'package:flutter/material.dart';
import '../util/service_box.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final double horizontalPadding = 25;
  final double verticalPadding = 25;

  // list of services
  List services = [
    ["Background\nRemover", "assets/background_remove.png", true],
    ["QR\nGenerator", "assets/qr.png", false],
    ["Scanner", "assets/scanner.png", false],
    ["Calculator", "assets/calculator.png", true],
    ["Text to\nSpeech", "assets/tts.png", false],
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[300],
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: EdgeInsets.symmetric(
                  horizontal: horizontalPadding, vertical: verticalPadding),
              child: IconButton(
                iconSize: 50,
                icon: const Icon(
                  Icons.tips_and_updates,
                ),
                onPressed: () {},
              ),
            ),
            const SizedBox(height: 20),
            Padding(
              padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text("Welcome to",
                    style: TextStyle(fontSize: 15, color: Colors.grey[700]),
                  ),
                  const Text(
                    "APPY TOOLBOX",
                    style: TextStyle(
                      fontSize: 37,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 25),

            Padding(
                padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: Divider(
                color: Colors.grey[500],
                thickness: 1,
              ),
            ),

            Padding(
              padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: const Text('Services'),
            ),
            Expanded(
                child: GridView.builder(
                  itemCount: services.length,
                    padding: const EdgeInsets.all(10.0),
                    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2,
                      childAspectRatio: 1/1.3,
                    ),
                    itemBuilder: (context, index) {
                      return ServiceBox(
                        serviceName: services[index][0],
                        iconPath: services[index][1],
                        color: services[index][2],
                      );
                    },
                )),
          ],
        ),
      ),
    );
  }
}

ServiceBox组件代码

import 'package:flutter/material.dart';

class ServiceBox extends StatelessWidget {
  final String serviceName;
  final String iconPath;
  final bool color;

  ServiceBox(
      {Key? key,
      required this.serviceName,
      required this.iconPath,
      required this.color})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(15.0),
      child: Container(
        decoration: BoxDecoration(
          color: color ? Colors.grey[900] : Colors.grey[200],
          borderRadius: BorderRadius.circular(24),
        ),
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 25.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Image.asset(iconPath, height: 50, color: color ? Colors.white : Colors.black,),
              Row(
                children: [
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.only(left: 20.0, right: 20.0),
                      child: Text(
                        serviceName,
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 17,
                          color: color ? Colors.white : Colors.black,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

实现方案

步骤1:修改ServiceBox组件,添加点击回调

给ServiceBox新增VoidCallback类型的参数,并用InkWell(带水波纹交互效果)包裹容器,绑定点击事件:

import 'package:flutter/material.dart';

class ServiceBox extends StatelessWidget {
  final String serviceName;
  final String iconPath;
  final bool color;
  final VoidCallback onTap; // 新增点击回调参数

  ServiceBox(
      {Key? key,
      required this.serviceName,
      required this.iconPath,
      required this.color,
      required this.onTap}) // 加入参数列表
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(15.0),
      // 用InkWell实现点击交互,水波纹圆角与容器一致
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(24),
        child: Container(
          decoration: BoxDecoration(
            color: color ? Colors.grey[900] : Colors.grey[200],
            borderRadius: BorderRadius.circular(24),
          ),
          child: Padding(
            padding: const EdgeInsets.symmetric(vertical: 25.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Image.asset(iconPath, height: 50, color: color ? Colors.white : Colors.black,),
                Row(
                  children: [
                    Expanded(
                      child: Padding(
                        padding: const EdgeInsets.only(left: 20.0, right: 20.0),
                        child: Text(
                          serviceName,
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 17,
                            color: color ? Colors.white : Colors.black,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

步骤2:更新HomePage,添加页面跳转逻辑

  1. 导入所有目标页面
  2. 扩展services列表,添加每个服务对应的目标页面构造器
  3. 在GridView的itemBuilder中,给ServiceBox传入点击回调,通过Navigator.push实现跳转:
import 'package:flutter/material.dart';
import '../util/service_box.dart';
// 替换成你实际的页面路径和类名
import '../pages/background_remover_page.dart';
import '../pages/qr_generator_page.dart';
import '../pages/scanner_page.dart';
import '../pages/calculator_page.dart';
import '../pages/text_to_speech_page.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final double horizontalPadding = 25;
  final double verticalPadding = 25;

  // 扩展列表,添加对应页面构造器
  List services = [
    ["Background\nRemover", "assets/background_remove.png", true, () => const BackgroundRemoverPage()],
    ["QR\nGenerator", "assets/qr.png", false, () => const QrGeneratorPage()],
    ["Scanner", "assets/scanner.png", false, () => const ScannerPage()],
    ["Calculator", "assets/calculator.png", true, () => const CalculatorPage()],
    ["Text to\nSpeech", "assets/tts.png", false, () => const TextToSpeechPage()],
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[300],
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: EdgeInsets.symmetric(
                  horizontal: horizontalPadding, vertical: verticalPadding),
              child: IconButton(
                iconSize: 50,
                icon: const Icon(
                  Icons.tips_and_updates,
                ),
                onPressed: () {},
              ),
            ),
            const SizedBox(height: 20),
            Padding(
              padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text("Welcome to",
                    style: TextStyle(fontSize: 15, color: Colors.grey[700]),
                  ),
                  const Text(
                    "APPY TOOLBOX",
                    style: TextStyle(
                      fontSize: 37,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 25),

            Padding(
                padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: Divider(
                color: Colors.grey[500],
                thickness: 1,
              ),
            ),

            Padding(
              padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
              child: const Text('Services'),
            ),
            Expanded(
                child: GridView.builder(
                  itemCount: services.length,
                    padding: const EdgeInsets.all(10.0),
                    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2,
                      childAspectRatio: 1/1.3,
                    ),
                    itemBuilder: (context, index) {
                      return ServiceBox(
                        serviceName: services[index][0],
                        iconPath: services[index][1],
                        color: services[index][2],
                        // 绑定点击跳转逻辑
                        onTap: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => services[index][3](),
                            ),
                          );
                        },
                      );
                    },
                )),
          ],
        ),
      ),
    );
  }
}

补充说明

  • 请确保导入的页面路径和类名与你的实际代码一致
  • 若不需要水波纹效果,可将InkWell替换为GestureDetector,用法完全一致
  • 页面较多时,推荐使用命名路由优化跳转逻辑,只需将services列表中的页面构造器替换为路由名称,再通过Navigator.pushNamed实现跳转

内容的提问来源于stack exchange,提问作者Riddhipratim Adhikari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 10:03:09