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

Flutter点击头像跳转个人主页:滑动面板收起及跳转异常问题

问题描述
  • 需求:点击主页头像图标时,直接跳转至用户个人主页(UserPage),无需显示包含已上传故事的上滑面板。
  • 现状:当前点击头像会跳转至UserHandling页面,该页面加载StoryProfile组件;StoryProfile通过SlidingUpPanel实现故事面板与个人主页的联动,仅当用户无故事时面板才会关闭。
  • 问题:尝试在跳转逻辑中直接用UserPage()替代UserHandling时出现红屏错误。

主页按钮代码

IconButton(
  icon: const Icon(
    Icons.perm_identity,
    color: Colors.black,
  ),
  onPressed: () {
    User user = FirebaseAuth.instance.currentUser!;
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => UserHandling(
          uid: user.uid,
        ),
      ),
    );
  })

UserHandling页面代码

class UserHandling extends StatefulWidget {
  final String uid;

  const UserHandling({super.key, required this.uid});

  @override
  State<UserHandling> createState() => UserHandlingState();
}

class UserHandlingState extends State<UserHandling> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: StoryProfile(
          uid: widget.uid,
          currentUserID: widget.uid,
        ),
      ),
    );
  }
}

StoryProfile页面代码

class StoryProfile extends StatefulWidget {
  final String uid;
  final String currentUserID;

  const StoryProfile({
    super.key,
    required this.uid,
    required this.currentUserID,
  });

  @override
  State<StoryProfile> createState() => StoryProfileState();
}

class StoryProfileState extends State<StoryProfile> {
  List<Story> storiesList = [];
  PanelController panelController = PanelController();
  Color bgColor = Colors.transparent;

  late String currentUser;

  @override
  void initState() {
    super.initState();
    currentUser = FirebaseAuth.instance.currentUser!.uid;
    FirebaseFirestore.instance
        .collection('Users/${widget.uid}/Story')
        .get()
        .then((value) {
      if (value.docs.isEmpty) {
        panelController.close();
      }
      for (var element in value.docs) {
        storiesList.add(Story(
          url: (element.data() as Map)['url'],
          media: (element.data() as Map)['media'],
          id: element.id,
        ));
      }
      setState(() {});
      getStoryData();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SlidingUpPanel(
        color: bgColor,
        controller: panelController,
        onPanelOpened: () => setState(() => bgColor = Colors.white),
        onPanelClosed: () => setState(() => bgColor = Colors.transparent),
        panelBuilder: (ScrollController sc) {
          UserPage pg = UserPage();
          pg.scrollController = sc;
          return pg;
        },
        collapsed: const Center(
          child: Column(
            children: [
              Icon(
                Icons.keyboard_arrow_up,
                color: Colors.white,
              ),
              Text(
                "SwipeUp to see more information",
                style: TextStyle(color: Colors.white),
              ),
            ],
          ),
        ),
        body: storiesList.isNotEmpty
            ? StoryScreen(
                stories: storiesList,
                uid: currentUser,
                currentUserID: currentUser,
              )
            : const Center(
                child: Text(
                  'Aici trebuie adaugate imagini/videoclipuri cu serviciile dvs. cu scopul de promovare\nFolositi butonul "adauga story" ',
                  textAlign: TextAlign.center,
                ),
              ),
      ),
    );
  }

  void getStoryData() async {
    QuerySnapshot<Map<String, dynamic>> query = await FirebaseFirestore.instance
        .collection('Users/$currentUser/Story')
        .get();
    storiesList.clear();
    if (query.docs.isNotEmpty) {
      for (DocumentSnapshot<Map> doc in query.docs) {
        setState(() {
          storiesList.add(Story.fromDocument(doc));
        });
      }
    }
  }
}

UserPage代码

class UserPage extends StatefulWidget {
  late ScrollController scrollController;

  UserPage({super.key});

  @override
  State<UserPage> createState() => UserProfile();
}

class UserProfile extends State<UserPage> {
  bool isFileImage = false;
  late User user;
  late File pickedFile;
  String? userImage;
  String? address;
  String? precise_address;
  String? userName;
  String? facebookUrl;
  String? instagramUrl;
  String? tikTokUrl;
  TextEditingController facebookController = TextEditingController();
  TextEditingController instagramController = TextEditingController();
  TextEditingController tiktokController = TextEditingController();
  late String userId;
  Random randomGenerator = Random();

  String? searchString;
  String? country;

  QuerySnapshot? querySnapshot;

  @override
  void initState() {
    super.initState();
    initUser();
  }

  initUser() async {
    user = FirebaseAuth.instance.currentUser!;
    userId = user.uid;
    logger.e("user id at init user =$userId");
    getSocialData();
    getData();
  }

  final TextEditingController _userNameController = TextEditingController();

  late File _image;
  final picker = ImagePicker();

  Future getImage() async {
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);
    if (pickedFile != null) {
      setState(() {
        _image = File(pickedFile.path);
      });
      Reference storageReference = FirebaseStorage.instance.ref();
      Uint8List imageData = _image.readAsBytesSync();
      storageReference
          .child("Users Profile")
          .child(userId)
          .putData(imageData)
          .then((uploadTask) async {
        var dowurl = await uploadTask.ref.getDownloadURL();
        String url = dowurl.toString();

        Map<String, dynamic> urlNull = {
          'avatarUrl': url,
        };
        DocumentReference prereference =
            FirebaseFirestore.instance.collection('Users').doc(userId);

        await FirebaseFirestore.instance.runTransaction((transaction) async {
          transaction.update(prereference, urlNull);
        });
      });
    }
  }

  Future<void> displayPrediction(Prediction p, BuildContext context) async {
    var placeId = p.placeId;
    var address = await FlGeocoder(Constants.googlePlaceKey)
        .findAddressesFromPlaceId(placeId!);
    setState(() {
      searchString = p.description!;
    });
    FirebaseFirestore.instance.collection('Users').doc(userId).update({
      "precise_location": searchString,
    });
    country = address.first.country!.longName;
  }

  var addresses;
  var first;
  double lat = 0.0;
  double long = 0.0;

  @override
  void didChangeDependencies() async {
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    void viewReview(var ratings, String name, String review, String avatarUrl) {
      showGeneralDialog(
        context: context,
        barrierDismissible: false,
        transitionDuration: const Duration(
          milliseconds: 400,
        ),
        pageBuilder: (_, __, ___) {
          return Container(
            color: Colors.black.withOpacity(0.4),
            child: Scaffold(
              backgroundColor: Colors.transparent,
              appBar: AppBar(
                backgroundColor: Colors.transparent,
                automaticallyImplyLeading: false,
                actions: [
                  IconButton(
                    icon: const Icon(
                      Icons.close,
                      color: Colors.white,
                    ),
                    onPressed: () {
                      Navigator.pop(context);
                    },
                  ),
                ],
              ),
              body: SafeArea(
                child: Container(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    child: ListView(
                      children: [
                        Container(
                          margin: const EdgeInsets.only(
                              right: 12, top: 8, bottom: 0),
                          padding: const EdgeInsets.all(11),
                          decoration: BoxDecoration(
                              color: Colors.white,
                              boxShadow: [
                                BoxShadow(
                                    color: Colors.black.withOpacity(0.1),
                                    blurRadius: 2,
                                    spreadRadius: 1)
                              ],
                              borderRadius: BorderRadius.circular(8)),
                          child: Column(
                            children: [
                              Row(
                                children: [
                                  CircleAvatar(
                                    backgroundImage: const AssetImage(
                                        'assets/images/profilepic.png'),
                                    child: Image.network(avatarUrl),
                                  ),
                                  const SizedBox(width: 8),
                                  Text(name,
                                      style: const TextStyle(
                                          fontWeight: FontWeight.bold)),
                                  const SizedBox(width: 50),
                                  Row(
                                    children: [
                                      _buildRatingStars(ratings),
                                    ],
                                  )
                                ],
                              ),
                              Container(
                                margin: const EdgeInsets.only(top: 8),
                                width: MediaQuery.of(context).size.width - 140,
                                child: Text(
                                  review,
                                  textScaleFactor: 1.1,
                                ),
                              ),
                              TextButton(
                                style: TextButton.styleFrom(
                                  shape: const StadiumBorder(),
                                  backgroundColor: Colors.black,
                                ),
                                onPressed: () {
                                  Navigator.pop(context);
                                },
                                child: const Text(
                                  'Close',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          );
        },
      );
    }

    return ListView(
      controller: widget.scrollController,
      padding: EdgeInsets.zero,
      children: <Widget>[
        SizedBox(
          height: MediaQuery.of(context).size.height * 0.32,
          child: Stack(
            children: <Widget>[
              Positioned(
                top: MediaQuery.of(context).size.height * 0.06,
                left: MediaQuery.of(context).size.width * 0.09,
                child: Row(
                  children: [
                    Center(
                      child: SizedBox(
                        height: 84,
                        width: 84,
                        child: userImage != 'default'
                            ? CircleAvatar(
                                radius: 10,
                                backgroundImage: NetworkImage(userImage ?? ""))
                            : const CircleAvatar(
                                radius: 10,
                                backgroundImage:
                                    AssetImage('assets/images/profilepic.png'),
                              ),
                      ),
                    ),
                  ],
                ),
              ),
              Positioned(
                right: 24,
                top: MediaQuery.of(context).size.height * 0.2,
                child: Row(
                  children: <Widget>[
                    SizedBox(
                      height: 32,
                      width: 100,
                      child: ElevatedButton(
                          onPressed: () async {
                            await Alert(
                              context: context,
                              title: "",
                              desc:
                                  "Adauga video-uri sau imagini cu afacerea ta",
                              buttons: [
                                DialogButton(
                                  onPressed: () => addPicture(uploadTypes.STORY,
                                      uploadTypes.IMAGE, context),
                                  width: 120,
                                  child: const Text(
                                    "Image",
                                    style: TextStyle(
                                        color: Colors.white, fontSize: 20),
                                  ),
                                ),
                                DialogButton(
                                  onPressed: () => addPicture(uploadTypes.STORY,
                                      uploadTypes.VIDEO, context),
                                  width: 120,
                                  child: const Text(
                                    "Video",
                                    style: TextStyle(
                                        color: Colors.white, fontSize: 20),
                                  ),
                                ),
                              ],
                            ).show();
                          },
                          child: const Text(
                            "Adauga Story",
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 12,
                            ),
                          )),
                    ),
                    const SizedBox(
                      width: 16,
                    ),
                    GestureDetector(
                      onTap: () async {
                        DocumentSnapshot snapshot = await FirebaseFirestore
                            .instance
                            .collection('Users')
                            .doc(userId)
                            .get();
                        UserModel user = UserModel.fromDocument(snapshot);
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) => SettingsPage(user: user)),
                        );
                      },
                      child: Container(
                        height: 32,
                        width: 32,
                        decoration: const BoxDecoration(
                            image: DecorationImage(
                                image: AssetImage("assets/images/settings.png"),
                                fit: BoxFit.cover),
                            shape: BoxShape.circle,
                            color: Colors.white),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
        const SizedBox(
          height: 20,
        ),
        Row(children: [
          const SizedBox(
            width: 30,
          ),
          Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  // 代码此处截断,保留原结构
                ],
              ),
            ],
          ),
        ]),
      ],
    );
  }
}

解决方案

红屏错误的核心原因是UserPage的scrollController被声明为late,但直接跳转时未初始化——之前该控制器由StoryProfile的SlidingUpPanel在panelBuilder中传入。

修改步骤:

  1. 调整UserPage的控制器声明
    将late ScrollController scrollController改为可选参数并提供默认逻辑:
class UserPage extends StatefulWidget {
  final ScrollController? scrollController;

  UserPage({super.key, this.scrollController});

  @override
  State<UserPage> createState() => UserProfile();
}
  1. 在UserProfile中处理控制器
    在build方法的ListView里,使用传入的控制器或创建默认控制器:
return ListView(
  controller: widget.scrollController ?? ScrollController(),
  padding: EdgeInsets.zero,
  // 其余代码保持不变
);
  1. 更新主页跳转逻辑
    直接跳转至UserPage,无需额外参数:
IconButton(
  icon: const Icon(
    Icons.perm_identity,
    color: Colors.black,
  ),
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => UserPage(),
      ),
    );
  })

修改后,UserPage既能在SlidingUpPanel中正常接收传入的控制器,也能独立跳转使用默认控制器,解决红屏问题的同时满足直接跳转个人主页的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:37:02