Flutterexperts

Empowering Vision with FlutterExperts' Expertise
Virtual 3D Tour In Flutter

In this article, we will explore the Virtual 3D Tour In Flutter. We see how to execute a demo program. We will tell you how to create an interactive virtual 3D tour and 360 view utilizing the panorama package, and how to work it in your Flutter applications.

  • For Panorama:

panorama package – All Versions
Pub is the package manager for the Dart programming language, containing reusable libraries & packages for Flutter and…pub.dev

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents::

Introduction

Implementation

Code Implement

Code File

Conclusion



Introduction:

We will explore virtual 3d tours and 360 views navigating through rooms, houses, kitchens, and spaces for a lifelike viewing experience. The below demo video shows how to create an interactive virtual 3d tour in Flutter and how a virtual 3d tour will work using the panorama package in your Flutter applications. 

We will show you user presses the button then opens a virtual screen then the user views 3D and 360 views. The user will touch the screen and swipe the image for view 360. It will be shown on your device.

Demo Module::


Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:
flutter:
sdk: flutter
panorama: ^0.4.1

Step 2: Import

import 'package:panorama/panorama.dart';

Step 3: Add the assets

Add assets to pubspec — yaml file.

assets:
- assets/images/

Step 4: Run flutter packages get in the root directory of your app.

How to implement code in dart file :

You need to implement it in your code respectively:

Create a new dart file called virtual_modeldart inside the lib folder.

In this virtual model, we will create two final String img and title. We will create a List of the virtual models are:

class VirtualModel {
final String img;
final String title;

VirtualModel(
{required this.img, required this.title,});

static List<VirtualModel> virtualModel = [
VirtualModel(
img: 'assets/images/house.jpg',
title: "Outside House",
),
VirtualModel(
img: 'assets/images/bedroom.jpg',
title: "Bedroom",
),
VirtualModel(
img: 'assets/images/kitchen.jpg',
title: "Kitchen",
),
VirtualModel(
img: 'assets/images/living_room.jpg',
title: "Living Room",
),

];

}

Create a new dart file called main.dart inside the lib folder.

In the main. dart file. We will create a MyHomePage class. In this class, we will add a Column widget on the body part. Inside the column, we will add an image then we will add a Stack widget. Inside the stack, we will add an image with fit, height, and width and wrap it to the FittedBox widget. Also, the FittedBox widget was wrapped to the ClipRRect widget.

Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.06,
),
Image.asset(
'assets/images/logo.png',
height: MediaQuery.of(context).size.height * 0.13,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.1,
),
Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left:6.0, right: 6.0),
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: FittedBox(
child: Image.asset(
"assets/images/house.jpg",
fit: BoxFit.cover,
height: MediaQuery.of(context).size.height * 0.4,
width: MediaQuery.of(context).size.width,
),
),
),
),
),
Positioned(
left: 0,
right: 0,
top: MediaQuery.of(context).size.height * 0.31,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const VirtualScreen()),
);
},
child: Center(
child: Container(
margin: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.28,),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)),
height: MediaQuery.of(context).size.height * 0.04,
child: Row(
children: [
Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
height: MediaQuery.of(context).size.height * 0.03,
width: MediaQuery.of(context).size.width * 0.1,
child: const Center(
child: Icon(
Icons.photo,
color: Colors.black,
size: 18,
)),
),
const Text(
"Virtual 3D Tour",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w700),
)
],
),
),
),
),
),
],
),
],
),

Also, we will add one more item on the Stack widget a container widget. In this widget, we will add an icon and text “Virtual 3D Tour” wrapped to it row widget on its child and the whole conatianer wrapped to the inkwell and navigate to the VirtualScreen(). Below we will deeply discuss the code.

When we run the application, we ought to get the screen’s output like the underneath screen capture.

Create a new class file called Virtual Screen inside the lib folder.

In the virtual screen, we will create a list of variables of the virtual model _virtual is equal to the VirtualModel.virtualModel and also, we will create a String of selectedImage.

final List<VirtualModel> _virtual = VirtualModel.virtualModel;
String? selectedImage;

In the body, we will add the Panorama method. In this method, we will add selectedImage not equal to null then show selectedImage else show a dummy image. Users will select the image to view 3D and 360 views.

Stack(
children: [
Panorama(
child: selectedImage != null
? Image.asset(selectedImage!)
: Image.asset("assets/images/house.jpg"),
),
],
),

Also, this below part was coming into the same stack widget. In this widget, we will add a row widget. In a row widget, we will add a MainAxisAlignment was spaceBetween then add an icon, text “Virtual 3D Tour”, and another icon was wrapped to its Positioned widget.

Positioned(
top: 20,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.7),
borderRadius: BorderRadius.circular(10)),
height: size.height * 0.05,
width: size.width * 0.12,
child: Center(
child: IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.arrow_back_ios_new_outlined,
color: Colors.black,
))),
),
const Text(
"Virtual 3D Tour",
style: TextStyle(color: Colors.white,fontSize: 18,fontWeight: FontWeight.w700),
),
Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.7),
borderRadius: BorderRadius.circular(10)),
height: size.height * 0.05,
width: size.width * 0.12,
child: Center(
child: IconButton(
onPressed: () {},
icon: const Icon(
Icons.more_vert,
color: Colors.black,
))),
),
],
),
),
),
Positioned(
bottom: 10,
left: 0,
right: 0,
child: _data()),

We will create another Positioned widget. In this widget, we will add its child was _data() method. Below we will deeply discuss this method.

Now we will discuss the _data() method.

In this method, we will add a return SizedBox widget. In this widget, we will add the ListView.separated method. In this method, we will add the itemCount was _virtual. length, itemBuilder we will return GestureDetector() widget. In this widget, we will add the onTap function. In this function, we will add that selectedImage is equal to the image and wrapped to it setState(). Its child we will add a _virtual[index].image was wrapped to the container.

Widget _data() {
return SizedBox(
height: MediaQuery.of(context).size.height* 0.15,
width: double.infinity,
child: ListView.separated(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _virtual.length,
itemBuilder: (context, index) {
String image = _virtual[index].img;
return GestureDetector(
onTap: () {
setState(() {
selectedImage = image;
});
},
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.white, width: 3)),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
_virtual[index].img,
fit: BoxFit.fill,
height: MediaQuery.of(context).size.height * 0.1,
width: MediaQuery.of(context).size.width * 0.25,
),
),
),
const SizedBox(
height: 10,
),
Text(
_virtual[index].title,
style: const TextStyle(fontSize: 17,fontWeight: FontWeight.w600),
),
],
),
),
);
},
separatorBuilder: (_, __) => const SizedBox(
width: 10,
),
));
}
When we run the application, we ought to get the screen’s output like the underneath screen capture.

Code File:

import 'package:flutter/material.dart';
import 'package:flutter_virtual_3d_tour_demo/splash_screen.dart';
import 'package:flutter_virtual_3d_tour_demo/virtual_model.dart';
import 'package:panorama/panorama.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
home: const Splash(),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.06,
),
Image.asset(
'assets/images/logo.png',
height: MediaQuery.of(context).size.height * 0.13,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.1,
),
Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left:6.0, right: 6.0),
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: FittedBox(
child: Image.asset(
"assets/images/house.jpg",
fit: BoxFit.cover,
height: MediaQuery.of(context).size.height * 0.4,
width: MediaQuery.of(context).size.width,
),
),
),
),
),
Positioned(
left: 0,
right: 0,
top: MediaQuery.of(context).size.height * 0.31,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const VirtualScreen()),
);
},
child: Center(
child: Container(
margin: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.28,),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)),
height: MediaQuery.of(context).size.height * 0.04,
child: Row(
children: [
Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
height: MediaQuery.of(context).size.height * 0.03,
width: MediaQuery.of(context).size.width * 0.1,
child: const Center(
child: Icon(
Icons.photo,
color: Colors.black,
size: 18,
)),
),
const Text(
"Virtual 3D Tour",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w700),
)
],
),
),
),
),
),

],
),
],
),
),
);
}
}

class VirtualScreen extends StatefulWidget {

const VirtualScreen({super.key,});

@override
State<VirtualScreen> createState() => _VirtualScreenState();
}

class _VirtualScreenState extends State<VirtualScreen> {
final List<VirtualModel> _virtual = VirtualModel.virtualModel;
String? selectedImage;

@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
body: Stack(
children: [
Panorama(
child: selectedImage != null
? Image.asset(selectedImage!)
: Image.asset("assets/images/house.jpg"),
),
Positioned(
top: 20,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.7),
borderRadius: BorderRadius.circular(10)),
height: size.height * 0.05,
width: size.width * 0.12,
child: Center(
child: IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.arrow_back_ios_new_outlined,
color: Colors.black,
))),
),
const Text(
"Virtual 3D Tour",
style: TextStyle(color: Colors.white,fontSize: 18,fontWeight: FontWeight.w700),
),
Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.7),
borderRadius: BorderRadius.circular(10)),
height: size.height * 0.05,
width: size.width * 0.12,
child: Center(
child: IconButton(
onPressed: () {},
icon: const Icon(
Icons.more_vert,
color: Colors.black,
))),
),
],
),
),
),
Positioned(
bottom: 10,
left: 0,
right: 0,
child: _data()),
],
),
);
}

Widget _data() {
return SizedBox(
height: MediaQuery.of(context).size.height* 0.15,
width: double.infinity,
child: ListView.separated(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _virtual.length,
itemBuilder: (context, index) {
String image = _virtual[index].img;
return GestureDetector(
onTap: () {
setState(() {
selectedImage = image;
});
},
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.white, width: 3)),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
_virtual[index].img,
fit: BoxFit.fill,
height: MediaQuery.of(context).size.height * 0.1,
width: MediaQuery.of(context).size.width * 0.25,
),
),
),
const SizedBox(
height: 10,
),
Text(
_virtual[index].title,
style: const TextStyle(fontSize: 17,fontWeight: FontWeight.w600),
),
],
),
),
);
},
separatorBuilder: (_, __) => const SizedBox(
width: 10,
),
));
}
}

Conclusion:

In the article, I have explained the Virtual 3D Tour in Flutter; you can modify this code according to your choice. This was a small introduction to the Virtual 3D Tour in Flutter User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on Trying Virtual 3D Tour in your Flutter projects. We will show you what the Introduction is. Make a demo program for working on the Virtual 3D Tour using the panorama package in your Flutter applications. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I need to correct something? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on Facebook, GitHub, Twitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.


Leave comment

Your email address will not be published. Required fields are marked with *.