flutterfire icon indicating copy to clipboard operation
flutterfire copied to clipboard

[firebase_database]: Issue on web debug mode when listening to multiple onValue streams on the same path

Open rohansohonee1 opened this issue 1 year ago • 1 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues.

Which plugins are affected?

Database

Which platforms are affected?

Web

Description

Listening to the same path multiple times will only return a result to the latest stream. However it must return a result to all streams i.e stream 1 & stream 2 print statements must get displayed.

This issue is present on web platform in DEBUG mode. The console display the following:

got stream 3 with value:null
got stream 3 with value:1
got stream 3 with value:2

When running in RELEASE mode there is no issue. The chrome console displays the following:

got stream 1 with value:2 js_primitives.dart:42 
got stream 2 with value:2 js_primitives.dart:42
got stream 3 with value:2 js_primitives.dart:42 
got stream 1 with value:3 js_primitives.dart:42 
got stream 2 with value:3 js_primitives.dart:42 
got stream 3 with value:3 js_primitives.dart:42 
got stream 1 with value:4 js_primitives.dart:42 
got stream 2 with value:4 js_primitives.dart:42 
got stream 3 with value:4 js_primitives.dart:42 

Lastly this worked fine in firebase_database 10.5.0 with firebase_core: 2.28.0 but is now broken in firebase_database: 11.0.4 with firebase_core 3.3.0.

Reproducing the issue

Run the code sample & increment the counter:

import 'dart:async';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

void main() async {
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  // returns the counter value
  Stream<int?> _getCounterValue() {
    return FirebaseDatabase.instance
        .ref()
        .child('/counter')
        .onValue
        .map((event) {
      final result = event.snapshot.value;
      return result as int?;
    });
  }

  int _counter = 0;

  // streams to listen to counter value
  StreamSubscription<int?>? _stream1, _stream2, _stream3;

  @override
  void initState() {
    super.initState();
    _stream1 = _getCounterValue().listen((value) {
      print('got stream 1 with value:$value');
      _updateCounter(value);
    });
    _stream2 = _getCounterValue().listen((value) {
      print('got stream 2 with value:$value');
      _updateCounter(value);
    });
    _stream3 = _getCounterValue().listen((value) {
      print('got stream 3 with value:$value');
      _updateCounter(value);
    });
  }

  void _updateCounter(int? value) {
    if (!mounted) return;
    setState(() => _counter = value ?? 0);
  }

  void _incrementCounter() {
    _counter++;
    FirebaseDatabase.instance.ref().child('/counter').set(_counter);
  }

  @override
  void dispose() {
    _stream1?.cancel();
    _stream2?.cancel();
    _stream3?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Firebase Core version

3.3.0

Flutter Version

3.19.6

Relevant Log Output

Launching lib/main.dart on Chrome in debug mode...
Waiting for connection from debug service on Chrome...
This app is linked to the debug service: ws://127.0.0.1:64942/82TkgPvGJ0s=/ws
Debug service listening on ws://127.0.0.1:64942/82TkgPvGJ0s=/ws
Debug service listening on ws://127.0.0.1:64942/82TkgPvGJ0s=/ws
got stream 3 with value:null
got stream 3 with value:1
got stream 3 with value:2

Flutter dependencies

Expand Flutter dependencies snippet

Dart SDK 3.3.4
Flutter SDK 3.19.6
web_debug_mode_issue 1.0.0+1

dependencies:
- cupertino_icons 1.0.8
- firebase_core 3.3.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- firebase_database 11.0.4 [firebase_core firebase_core_platform_interface firebase_database_platform_interface firebase_database_web flutter]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]

dev dependencies:
- flutter_lints 3.0.2 [lints]
- flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service]

transitive dependencies:
- _flutterfire_internals 1.3.40 [collection firebase_core firebase_core_platform_interface flutter meta]
- async 2.11.0 [collection meta]
- boolean_selector 2.1.1 [source_span string_scanner]
- characters 1.3.0
- clock 1.1.1
- collection 1.18.0
- fake_async 1.3.1 [clock collection]
- firebase_core_platform_interface 5.2.0 [collection flutter flutter_test meta plugin_platform_interface]
- firebase_core_web 2.17.4 [firebase_core_platform_interface flutter flutter_web_plugins meta web]
- firebase_database_platform_interface 0.2.5+40 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface]
- firebase_database_web 0.2.5+12 [collection firebase_core firebase_core_web firebase_database_platform_interface flutter flutter_web_plugins]
- flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math]
- leak_tracker 10.0.0 [clock collection meta path vm_service]
- leak_tracker_flutter_testing 2.0.1 [flutter leak_tracker leak_tracker_testing matcher meta]
- leak_tracker_testing 2.0.1 [leak_tracker matcher meta]
- lints 3.0.0
- matcher 0.12.16+1 [async meta stack_trace term_glyph test_api]
- material_color_utilities 0.8.0 [collection]
- meta 1.11.0
- path 1.9.0
- plugin_platform_interface 2.1.8 [meta]
- sky_engine 0.0.99
- source_span 1.10.0 [collection path term_glyph]
- stack_trace 1.11.1 [path]
- stream_channel 2.1.2 [async]
- string_scanner 1.2.0 [source_span]
- term_glyph 1.2.1
- test_api 0.6.1 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph]
- vector_math 2.1.4
- vm_service 13.0.0
- web 0.5.1


Additional context and comments

This issue has been reported previously here. I also added a comment to that issue. However the issue was locked.

So I have opened a new issue with sample code to reproduce it.

rohansohonee1 avatar Aug 14 '24 11:08 rohansohonee1

We also have this issue - seems to only be on web. We have had to lock our firebase_database to 10.5.7, everything higher than this is broken.

bwhiteFC avatar Aug 15 '24 20:08 bwhiteFC

@Lyokone Is there a way to track when this fix will be released? It is still broken for us on firebase_database: 11.1.3 and it is preventing us from updating our firebase packages.

bwhiteFC avatar Sep 18 '24 19:09 bwhiteFC

@bwhiteFC Are you using Riverpods by any chance? We had this problem and determined that it was actually due to an issue in the latest release of that package and had to roll back to flutter_riverpod version 2.0.0.

ansells avatar Sep 25 '24 22:09 ansells

@ansells @Lyokone no I'm not using riverpods. This is specifically an issue on web only where if you have the following code:

Future<String?> getUserName() async {
    final snapshot = await _databaseReference
        .child('users/$userId/name')
        .once();
    return snapshot.snapshot.value as String?;
  }
  
Stream<String?>  get observeUserName  => _databaseReference
              .child('users/$userId/name')
              .onValue.map((event) => event.snapshot.value as String?);

// Somewhere in the project you have:
final subscription = observeUserName.listen((event) => print(event));

// Somewhere later in the project you have:
final name = await getUserName();
print(name);

Doing this, getUserName() will never return because ever gets put into the stream so once() doesn't work like it should. This works fine on Android and iOS but it is broken in web starting with firebase_database 11.0.0.

bwhiteFC avatar Sep 26 '24 15:09 bwhiteFC