-1

I am creating an app like snapchat where the app start with camera page and before this it ask user for camera and microphone permission if the user grant both permission then the camera widget will be displayed other wise instead of camera widget center text button is being displayed that open the app settings

Now the issue is if the user grant the permission and i navigate to other tabs or close and re-open the app i saw the text button instead of camera for like very short interval of micro-seconds I don't want this behavior if the permission is granted just show camera page at once if not granted then show the center text button

Anyone can tell me how to solve this:

Here is my CameraPreview class code:

 import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

class CameraaPreview extends StatefulWidget {
  final List<CameraDescription> cameras;
  const CameraaPreview(
      {super.key, required this.cameras, required this.permissionStatus});
  final bool permissionStatus;

  @override
  State<CameraaPreview> createState() => _CameraaPreviewState();
}

class _CameraaPreviewState extends State<CameraaPreview> {
  late CameraController _cameraController;
  Future<void>? _intializeFutureController;
  XFile? image;
  bool havePic = false;
  bool haveVide = false;
  bool _isRecording = false;
  late Timer _timer;
  bool isBackCameraOpen = true;
  int _recordedSeconds = 0;
  bool _permissionsGranted = false;

  @override
  void initState() {
    super.initState();
    requestCameraAndMicrophonePermissions().then((granted) {
      if (granted) {
        setState(() {
          _permissionsGranted = true;
          _cameraController = CameraController(
              widget.cameras[0], ResolutionPreset.max,
              imageFormatGroup: ImageFormatGroup.bgra8888);
          _intializeFutureController = _cameraController.initialize();
        });
      }
    });
  }

  @override
  void dispose() {
    _cameraController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final Size height = MediaQuery.of(context).size;
    const double navBarHeight = kBottomNavigationBarHeight;
    final avatarSize = height.width * 0.0999;
    final avataPostion = (navBarHeight - avatarSize) / 0.35;

    if (!_permissionsGranted) {
      return Center(
        child: TextButton(
          style: TextButton.styleFrom(backgroundColor: Colors.transparent),
          onPressed: () {
            openAppSettings();
          },
          child: const Text(
            'Grant Permissions for Camera and Microphone to continue',
            style: TextStyle(
              color: Colors.black,
            ),
          ),
        ),
      );
    }

    if (_permissionsGranted) {
      _permissionsGranted = true;
      _cameraController = CameraController(
          widget.cameras[0], ResolutionPreset.max,
          imageFormatGroup: ImageFormatGroup.bgra8888);
      _intializeFutureController = _cameraController.initialize();
    }

    return havePic
        ? _showImage(image)
        : RepaintBoundary(
            child: FutureBuilder(
              future: _intializeFutureController,
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return const Center(
                    child: CircularProgressIndicator(
                      color: Colors.yellow,
                    ),
                  );
                } else if (snapshot.hasError) {
                  return Center(
                    child: Text('Error: ${snapshot.error}'),
                  );
                } else {
                  return Stack(
                    children: <Widget>[
                      SizedBox(
                        height: MediaQuery.of(context).size.height,
                        width: MediaQuery.of(context).size.width,
                        child: AspectRatio(
                          aspectRatio: _cameraController.value.aspectRatio,
                          child: CameraPreview(
                            _cameraController,
                          ),
                        ),
                      ),
                      Positioned(
                        bottom: avataPostion,
                        left: height.width / 2.25 - avatarSize / 2,
                        child: GestureDetector(
                          onLongPressStart: (_) => _startRecording(),
                          onLongPressEnd: (_) => _stopRecording(),
                          onTap: () => _clickPrecture(),
                          child: CircleAvatar(
                            radius: avatarSize,
                            backgroundColor: Colors.transparent,
                            child: Container(
                              decoration: BoxDecoration(
                                  border:
                                      Border.all(color: Colors.white, width: 4),
                                  borderRadius:
                                      BorderRadius.circular(avatarSize)),
                            ),
                          ),
                        ),
                      ),
                      Positioned(
                        top: 40,
                        left: height.width / 1.18,
                        child: GestureDetector(
                          onLongPressStart: (_) {
                            _startRecording();
                          },
                          onLongPressEnd: (_) {
                            _stopRecording();
                          },
                          child: IconButton(
                            onPressed: () {
                              setState(() {
                                isBackCameraOpen
                                    ? _openFrontCam()
                                    : _openBackCam();
                              });
                            },
                            icon: const Icon(
                              Icons.crop_rotate,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ),
                      if (_isRecording)
                        Positioned(
                          top: 20,
                          left: height.width / 2.25,
                          child: Text(
                            '$_recordedSeconds s',
                            style: const TextStyle(
                              color: Colors.red,
                              fontSize: 24,
                            ),
                          ),
                        ),
                    ],
                  );
                }
              },
            ),
          );
  }

  Future<bool> requestCameraAndMicrophonePermissions() async {
    Map<Permission, PermissionStatus> statuses = await [
      Permission.camera,
      Permission.microphone,
    ].request();

    return (statuses[Permission.camera] == PermissionStatus.granted &&
        statuses[Permission.microphone] == PermissionStatus.granted);
  }

  void _clickPrecture() async {
    if (_cameraController.value.isInitialized) {
      image = await _cameraController.takePicture();
      setState(() {
        havePic = !havePic;
      });
    }
  }

  void _startRecording() async {
    if (_cameraController.value.isInitialized) {
      _cameraController.startVideoRecording();
      _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
        setState(() {
          _recordedSeconds += 1;
        });
      });
      setState(() {
        _isRecording = true;
      });
    }
  }

  void _stopRecording() async {
    if (_cameraController.value.isRecordingVideo) {
      _timer.cancel();
      _recordedSeconds = 0;
      XFile videoFile = await _cameraController.stopVideoRecording();
      setState(() {
        _isRecording = false;
      });
    }
  }

  void _openFrontCam() {
    _cameraController = CameraController(
        widget.cameras[1], ResolutionPreset.max,
        imageFormatGroup: ImageFormatGroup.bgra8888);
    _intializeFutureController = _cameraController.initialize();
    isBackCameraOpen = !isBackCameraOpen;
  }

  void _openBackCam() {
    _cameraController = CameraController(
        widget.cameras[0], ResolutionPreset.max,
        imageFormatGroup: ImageFormatGroup.bgra8888);
    _intializeFutureController = _cameraController.initialize();
    isBackCameraOpen = !isBackCameraOpen;
  }

  _showImage(XFile? image) {
    return Stack(
      children: [
        SizedBox(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Image.file(
            File(image!.path),
            fit: BoxFit.cover,
          ),
        ),
      ],
    );
  }
}

here is my homePage class Code:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:ssnnaappcchhaatt/ios_screens/camera_preview.dart';
import 'package:ssnnaappcchhaatt/ios_screens/chat_page.dart';
import 'package:ssnnaappcchhaatt/ios_screens/map_page.dart';
import 'package:ssnnaappcchhaatt/ios_screens/play_page.dart';
import 'package:ssnnaappcchhaatt/ios_screens/search_page.dart';
import 'package:ssnnaappcchhaatt/ios_screens/stories_page.dart';

class HomePage extends StatefulWidget {
  final List<CameraDescription> cameras;
  final bool permissionStatus;
  static const homePage = 'homePage/';
  const HomePage(
      {super.key, required this.cameras, required this.permissionStatus});

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

class _HomePageState extends State<HomePage> {
  int currentIndex = 2;
  bool cameraShow = true;

  @override
  Widget build(BuildContext context) {
    final screens = [
      const MapPage(),
      const ChatPage(),
      !cameraShow
          ? const SearchPage()
          : CameraaPreview(
              cameras: widget.cameras,
              permissionStatus: widget.permissionStatus,
            ),
      const StoriesPage(),
      const PlayPage()
    ];
    return Scaffold(
      backgroundColor: const Color.fromARGB(255, 169, 154, 154),
      body: screens[currentIndex],
      bottomNavigationBar: BottomNavigationBar(
          items: [
            const BottomNavigationBarItem(
              label: "Map",
              icon: Icon(
                CupertinoIcons.map_pin,
                size: 24,
              ),
            ),
            const BottomNavigationBarItem(
              label: "Chat",
              icon: Icon(
                CupertinoIcons.chat_bubble,
                size: 24,
              ),
            ),
            BottomNavigationBarItem(
              label: !cameraShow ? "Camera" : "Search",
              icon: !cameraShow
                  ? GestureDetector(
                      onTap: () => {
                        Navigator.of(context)
                            .pushReplacementNamed(HomePage.homePage)
                      },
                      child: const Icon(
                        CupertinoIcons.camera,
                        size: 24,
                      ),
                    )
                  : const Icon(
                      CupertinoIcons.search,
                      size: 24,
                    ),
            ),
            const BottomNavigationBarItem(
              label: "Stories",
              icon: Icon(
                CupertinoIcons.group,
                size: 30,
              ),
            ),
            const BottomNavigationBarItem(
              label: "play",
              icon: Icon(
                CupertinoIcons.play,
                size: 24,
              ),
            )
          ],
          type: BottomNavigationBarType.fixed,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          backgroundColor: Colors.transparent,
          unselectedItemColor: Colors.black,
          selectedItemColor: Colors.blue,
          currentIndex: currentIndex,
          onTap: (value) {
            if (value == 2 || currentIndex == 2) {
              setState(() {
                currentIndex = value;
                cameraShow = !cameraShow;
              });
            } else {
              setState(() {
                currentIndex = value;
              });
            }
          }),
    );
  }
}

here is my main class code:

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

import 'package:ssnnaappcchhaatt/ios_screens/home_page.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final cameras = await availableCameras();
  runApp(MyApp(
    cameras: cameras,
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.cameras});
  final List<CameraDescription> cameras;
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Snap Chat',
      debugShowCheckedModeBanner: false,
      home: HomePage(
        cameras: cameras,
        permissionStatus: false,
      ),
      routes: {
        HomePage.homePage: (context) => HomePage(
              cameras: cameras,
              permissionStatus: false,
            ),
      },
    );
  }
}
RIZWAN ALI
  • 120
  • 1
  • 1
  • 11
  • Please [edit] your question title to describe the problem you're having or question you're asking. The title should be clear and descriptive enough to convey information about the post content to a future site user who is skimming a list of search results trying to find a solution to a problem. Your current title is nothing but a repetition of the tags (which should not even be in the title). For some suggestions on how to write a good title, see the [ask] page. – Ken White May 02 '23 at 01:23
  • I would recommend breaking big codes into chunks while posting of stackoverflow. helps in readability... – princeoo7 May 02 '23 at 02:31

1 Answers1

1

You need to add condition for atleast 3 states of FutureBuilder here

  1. Waiting for data
  2. Received Data
  3. Error receiving data

In your implementation I see only two. Number 2 and 3. There is no condition for 1. So while your FutureBuilder is waiting for the data, you see the TextButton as well.

You could try with the below minimalist logic:

     if (snapshot.hasData) {
             //Return your main widget here
             //Add a conditional here based on data to show either Camera or TextButton
         } else if (snapshot.hasError) {
              //Return your error widget here
         } else {
             //Return your waiting for data widget here
         }