How to remote end call or broadcast in flutter for agora video call like, for example:
import 'package:flutter/material.dart';
class VideoCalling extends StatelessWidget {
const VideoCalling({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
late RtcEngine _engine;
return Scaffold(
body: ElevatedButton(onPressed: (){
if(_remoteUser.isFirst) async {
await _engine.remoteDestroy();
await _engine.destroy();
await _engine.leave().then(value) => Navigator.pop(context);
} else{
await _engine.destroy();
await _engine.leave().then(value) => Navigator.pop(context);
}
}, child: Text("End all calls")),
);
}
}
Related
Minimal reproducible code:
StreamController<int> _streamController = StreamController();
final provider1 = StreamProvider<int?>((ref) {
return _streamController.stream.map((value) {
print('listener1()');
return value;
});
});
final provider2 = StreamProvider<int?>((ref) {
return ref.watch(provider1).maybeWhen(
orElse: () => Stream.value(null),
data: (value) {
print('listener2()');
return Stream.value(value);
},
);
});
class FooPage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(provider2);
return Scaffold(
body: ElevatedButton(
onPressed: () async {
await _streamController.addStream(Stream.value(42));
print('Done');
},
child: Text('Button'),
),
);
}
}
When you press the button, it prints:
I/flutter (15845): listener1()
I/flutter (15845): Done
I/flutter (15845): listener2()
But the correct order should be:
I/flutter (15845): listener1()
I/flutter (15845): listener2()
I/flutter (15845): Done
So, how can I fix this delay? Who is causing it?
I change code like below only delegating firebase job to add1()
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
const ProviderScope(child: MaterialApp(home: MyApp())),
);
}
final StreamController<int> streamController = StreamController.broadcast();
final provider1 = StreamProvider<int>((ref) {
return streamController.stream.map((e) {
print("provider1 : receive event");
return e;
});
});
final provider2 = StreamProvider<int>((ref) {
return ref.watch(provider1).maybeWhen(
orElse: () => Stream.value(-1),
data: (user) {
print('provider2 : value received from provider1');
return Stream.value(user);
},
);
});
class MyApp extends ConsumerStatefulWidget {
const MyApp({
Key? key,
}) : super(key: key);
#override
ConsumerState createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
Future<void> add1() async {
await Future.delayed(const Duration(seconds: 1));
streamController.add(1);
await Future.delayed(Duration.zero); // HERE!
}
#override
Widget build(BuildContext context) {
ref.watch(provider2);
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () async {
await add1();
print("button : clicked");
},
child: Text("click"),
),
),
);
}
}
As you can see in add1(), there is await Future.delayed(Duration.zero) at last.
If click the button, there are two results.
provider1 : receive event
provider2 : value received from provider1
button : clicked
and
provider1 : receive event
button : clicked
provider2 : value received from provider1
But if this line is removed, you can see as you want
provider1 : receive event
provider2 : value received from provider1
button : clicked
So I think the reason is in the logic firebase work.
I saved the data using SharedPreferences, when my app is starting i try check type of user "client" or "company" after that wil be shown screen.
Future<void> setTypeClient() async {
final _storage = SharedPreferences.getInstance();
final storage = await _storage;
storage.setString('type_user_db', 'client');
}
My app is starting
I have variable
var typeUser = ' ' ; and use it when I determine which screen to display
Аfter I get the data from SharedPreferences, I need to put value in this variable
But i have an error, cause variable remains empty
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool isLogOut = true;
#override
void initState() {
if (FirebaseAuth.instance.currentUser != null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
update
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool get isLogOut => typeUser.isEmpty;
#override
void initState() {
if (FirebaseAuth.instance.currentUser == null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
error in
setState(() {
isLogOut = false;
});
error screen
enter image description here
To solve this issue we have to clarify synchronous and asynchronous operations.
Initialating you widget with initState and next build method called one by one in synchronous way but getUser is asynchronous.
This is meant that you specify isLogOut before you setting the typeUser.
Instead of manually setting the isLogOut property just to change it to getter
bool get isLogOut => typeUser.isEmpty;
And you can guarantee the property will be correct all the time when you requests it.
Enjoy coding
I have web api that returns last image taken from database so I wish to make timer to show latest image every few seconds using Flutter and Dart. Ideally I would like to load image, shot it to the user and request new image from server infinitely. My issue is that before new image is displayed, black screen appears so it makes flickering effect. How can I solve it? Here is my code:
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
#override
_CameraWidgetState createState() => _CameraWidgetState();
}
class _CameraWidgetState extends State<TestWidget> {
late int a = 1;
final scaffoldKey = GlobalKey<ScaffoldState>();
late bool loaded = false;
late Future futureRecords;
Future getImage() async {
final responses = await http.get(
Uri.parse("https://0465-95-178-160-106.ngrok.io/profile/download?picNum=" +
(a++).toString()),
headers: {
"Accept": "application/json",
"Access-Control_Allow_Origin": "*",
});
if (responses.statusCode == 200) {
return responses.bodyBytes;
} else {
throw Exception('Failed to load image');
}
}
#override
void initState() {
futureRecords = getImage();
Timer.periodic(Duration(seconds: 5), (timer) async {
try {
//await getImage();
setState(() {
futureRecords = getImage();
});
} catch (Ex) {}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureRecords,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
Uint8List bytes = snapshot.data as Uint8List;
return Image.memory(
bytes,
fit: BoxFit.fill,
);
});
}
}
Solution
You wrap your Testwidget with Material or Scaffold,Materialapp widget.You can AnimatedSwitcher widget to avoid that flickering effect or default color
Material(
child: AnimatedSwitcher(
duration: Duration(seconds: 1),
child: widget,
),
);
Before (Flickering)
class MYAppWithFlicker extends StatelessWidget {
const MYAppWithFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TestWidget();
}
}
After (Without Flickering)
class MYAppWithoutFlicker extends StatelessWidget {
const MYAppWithoutFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(child: TestWidget());
}
}
Sample Code
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
void main() {
runApp(MYAppWithoutFlicker());
}
class MYAppWithoutFlicker extends StatelessWidget {
const MYAppWithoutFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
color: Colors.lightGreen,
child: TestWidget(),
);
}
}
class MYAppWithFlicker extends StatelessWidget {
const MYAppWithFlicker({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TestWidget();
}
}
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
#override
_CameraWidgetState createState() => _CameraWidgetState();
}
class _CameraWidgetState extends State<TestWidget> {
late int a = 1;
final scaffoldKey = GlobalKey<ScaffoldState>();
late bool loaded = false;
late Future futureRecords;
Future getImage() async {
var s = "https://0465-95-178-160-106.ngrok.io/profile/download?picNum=" +
(a++).toString();
final responses = await get(
Uri.parse(a % 2 == 0
? "https://images.indianexpress.com/2019/09/toys.jpg"
: "https://ecommerce.ccc2020.fr/wp-content/uploads/2020/10/electronic-gadgets.jpeg"),
headers: {
"Accept": "application/json",
"Access-Control_Allow_Origin": "*",
});
if (responses.statusCode == 200) {
print(responses.bodyBytes);
return responses.bodyBytes;
} else {
throw Exception('Failed to load image');
}
}
#override
void initState() {
futureRecords = getImage();
Timer.periodic(Duration(seconds: 5), (timer) async {
try {
//await getImage();
setState(() {
futureRecords = getImage();
});
} catch (Ex) {}
});
super.initState();
}
#override
Widget build(BuildContext context) {
var widget;
return FutureBuilder(
future: futureRecords,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
widget =
Center(child: CircularProgressIndicator());
else if (snapshot.connectionState == ConnectionState.done) {
Uint8List bytes = snapshot.data as Uint8List;
widget = Image.memory(
bytes,
fit: BoxFit.fill,
);
}
// return AnimatedSwitcher(
// duration: Duration(seconds: 1),
// child: widget,
// );
return Material(
child: AnimatedSwitcher(
duration: Duration(seconds: 1),
child: widget,
),
);
});
}
}
After few days of trying various methods, I found out about gaplessPlayback property in Image.memory and when I set it to true, all magically worked as expected.
I am trying to apply conditions on widget when user is logged in, display widget logout else display login button.How is it possible?
Before login
After Login
I used shred_preferences package saving, getting and removing used data.
Here my codes:
shared_preferences
import 'package:shared_preferences/shared_preferences.dart';
class PrefServices{
Future createCache(String username, String password) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
_preferences.setString("username", username);
_preferences.setString("password", password);
}
Future readCache(String username) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
var username = _preferences.getString("username")?? "null";
// _preferences.getString("password");
return username;
}
Future<void> removeCache(String username, String password) async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
_preferences.remove("username");
_preferences.remove("password");
}
}
Profile Screen
import 'dart:async';
import 'package:clothing_roll/shred_preferences/shred_preferences_services.dart';
import 'package:clothing_roll/ui/widget/profile/login_widget.dart';
import 'package:clothing_roll/ui/widget/profile/profiles_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ProfileScreen extends StatefulWidget {
const ProfileScreen({ Key? key }) : super(key: key);
#override
_ProfileScreen createState() => _ProfileScreen();
}
class _ProfileScreen extends State<ProfileScreen> {
final PrefServices _prefServices = PrefServices();
#override
void initState() {
_prefServices.readCache("username").then((value) {
print(value.toString());
if (value != null) {
return Timer(Duration(seconds: 1),
() => ProfileWidget());
} else {
return Timer(Duration(seconds: 1),
() => LoginWidget());
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
Note: I have used Getx package
create a bool variable for conmdition check
add ternary operator to bool variable and change the conditions
class Test1 extends StatelessWidget {
Controller controller = Get.put(Controller());
Test1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
width: Get.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
controller.isLogged.value = !controller.isLogged.value;
},
child: Text("Press to change value")),
Obx(
() => controller.isLogged.value
? ElevatedButton(onPressed: () {}, child: Text("logged In"))
: ElevatedButton(onPressed: () {}, child: Text("Logged Out")),
),
],
),
),
);
}
}
I'm learning bloc from a deprecated tutorial and I have troubles with making it work.
The error which I receive is:
Error while creating GetRandomNumberTrivia
(...)
The following StateError was thrown building InheritedProvider<NumberTriviaBloc>:
Bad state: Tried to read a provider that threw during the creation of its value.
The exception occurred during the creation of type NumberTriviaBloc.
Full error: https://pastebin.com/CpvrFZ1v
This is my bloc file:
const String serverFailureMessage = 'Server failure';
const String cacheFailureMessage = 'Cache failure';
const String invalidInputMessage =
'Invalid input, the number should be positive integer or zero';
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
final GetConcreteNumberTrivia getConcreteNumberTrivia;
final GetRandomNumberTrivia getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc(
{required this.getConcreteNumberTrivia,
required this.getRandomNumberTrivia,
required this.inputConverter})
: super(Empty()) {
on<GetTriviaForConcreteNumber>(
(event, emit) async {
final inputEither =
inputConverter.stringToUnsignedInteger(event.numberString);
inputEither.fold((l) => emit(Error(message: invalidInputMessage)),
(r) async {
emit(Loading());
final failureOrTrivia = await getConcreteNumberTrivia(Params( number: r));
//todo message
failureOrTrivia.fold((l) => emit(Error(message: 'problem message')),
(r) => emit(Loaded(trivia: r)));
});
},
);
on<GetTriviaForRandomNumber>(
(event, emit) async {
emit(Loading());
final failureOrTrivia = await getRandomNumberTrivia(NoParams());
failureOrTrivia.fold((l) => emit(Error(message: "Invalid input")),
(r) async {
emit(Loaded(trivia: r));
});
},
);
}
}
Here's code which I mimic: https://github.com/ResoCoder/flutter-tdd-clean-architecture-course/blob/master/lib/features/number_trivia/presentation/bloc/number_trivia_bloc.dart
That's how I'm using BlocProvider:
class NumberTriviaPage extends StatelessWidget {
const NumberTriviaPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
(...)
body: buildBody(context),
);
}
}
BlocProvider<NumberTriviaBloc> buildBody(BuildContext context) {
return BlocProvider(
create: (_) => sl<NumberTriviaBloc>(),
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: <Widget>[
const SizedBox(height: 10),
BlocBuilder<NumberTriviaBloc, NumberTriviaState>(
builder: (context, state) {
if (state is Empty) {
return const MessageDisplay(
message: 'Start searching!',
);
} else if (state is Loading) {
return const LoadingWidget();
} else if (state is Loaded) {
return TriviaDisplay(numberTrivia: state.trivia);
} else if (state is Error) {
return MessageDisplay(
message: state.message);
}else{
return Container();
}
},
),
const TriviaControls(),
],
),
),
),
);
}
And that's how I init bloc, by using dependency injection:
import 'injection_container.dart' as di;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await di.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
(...)
home: const NumberTriviaPage(),
);
}
}
//sl = serviceLocator
final sl = GetIt.instance;
Future<void> init() async {
//serviceLocator call looking for are registered types/dependencies
sl.registerFactory(() => NumberTriviaBloc(getConcreteNumberTrivia: sl(), getRandomNumberTrivia: sl(), inputConverter: sl()));
(...)
}
According to error the problem seems to lays somewhere in GetRandomNumberTrivia, but I implemented it in same way as similar GetConcreteNumberTrivia and I do not see what can be wrong here.
In class GetRandomNumberTrivia I added typing NumberTriviaRepository for the field repository and the code compiled.