Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
682 views
in Technique[技术] by (71.8m points)

flutter - setState from child widget (drawer) not updating a widget above it in the tree (FloatingAppBar)

I have a flutter project with a FloatingSearchBar from this library (pub.dev). I have four buttons, one of which is a GestureDetector (which is not really important but explains some behind scenes info).

This all looks like this:

Search Bar

or this:

Search Bar

or one other variation.

These buttons all work fine, although I've spent a lot of time getting them that way. They are toggleable, and their Icons are decided by a variable that can be updated using setState.

There's also, however, this layout:

Offline Mode Search Bar

which should be able to be toggled on and off by this switch/button inside the menu:

Offline Mode Toggle Switch

It too just updates the offlineMode variable inside a setState. But this setState doesn't update the search bar until I force the bar to update by clicking any of the buttons.

Why is setState not working in this particular circumstance?

Code for search bar:

Widget buildFloatingSearchBar(AsyncSnapshot snapshot) {
    const bool isPortrait = true;

    return DescribedFeatureOverlay( //Don't worry about these I don't think
      featureId: 'search',
      tapTarget: Icon(Icons.search),
      title: Text('Search'),
      description: Text(
          'Tap the bar at the top to open the search interface, where you can search for GeoTags by name, approximate location or by author.'),
      overflowMode: OverflowMode.extendBackground,
      backgroundColor: Theme.of(context).primaryColor,
      targetColor: Colors.white,
      textColor: Colors.white,
      child: FloatingSearchBar(
        borderRadius: BorderRadius.all(
          Radius.circular(50),
        ),
        progress: loadingSearch,
        title: offlineMode //Checking offlineMode variable Should update with setState
            ? Row(
                children: [
                  Text(
                    'Geotagger',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 15,
                    ),
                  ),
                  Text(
                    ' - Offline Mode',
                    style: TextStyle(
                      //fontWeight: FontWeight.bold,
                      fontSize: 15,
                    ),
                  ),
                ],
              )
            : null,
        backgroundColor: Theme.of(context).backgroundColor,
        hint: 'Find a GeoTag...', //Shown if offlineMode is off (title is null)
        transitionDuration: const Duration(milliseconds: 800),
        transitionCurve: Curves.easeInOut,
        physics: const BouncingScrollPhysics(),
        axisAlignment: isPortrait ? 0.0 : -1.0,
        openAxisAlignment: 0.0,
        maxWidth: isPortrait ? 600 : 500,
        height: compassExpanded ? 62 : 48,
        debounceDelay: const Duration(milliseconds: 1500),
        onQueryChanged: (inQuery) {
          String query = inQuery.trim();
          if (offlineMode || query == null || query == '' || query == '@') {
            return;
          }
          if (query.startsWith('@')) {}
          setState(() {
            loadingSearch = true;
          });

          // Call your model, bloc, controller here.
          Future.delayed(const Duration(seconds: 3), () {
            setState(() {
              loadingSearch = false;
            });
          });
        },
        // Specify a custom transition to be used for
        // animating between opened and closed stated.
        transition: ExpandingFloatingSearchBarTransition(),
        accentColor: Theme.of(context).accentColor,
        actions: [
          FloatingSearchBarAction.searchToClear(
            showIfClosed: false,
          ),
          FloatingSearchBarAction( //This one is to check current GPS state
            showIfOpened: false,
            child: StreamBuilder<Object>(
                stream: Stream.periodic(Duration(seconds: 5), (x) => x),
                builder: (context, _) {
                  return FutureBuilder(
                      future: Geolocator.getCurrentPosition(
                        desiredAccuracy: LocationAccuracy.medium,
                        timeLimit: Duration(seconds: 5),
                      ),
                      builder: (context, compassDir) {
                        if (compassDir.hasError) {
                          //print('failure');
                          return Padding(
                            padding: const EdgeInsets.only(
                              right: 7,
                              bottom: 2,
                            ),
                            child: Icon(
                              Icons.wrong_location,
                              color: Colors.red,
                            ),
                          );
                        } else {
                          return Container(width: 0, height: 0);
                        }
                      });
                }),
          ),
          FloatingSearchBarAction(
            showIfOpened: false,
            child: DescribedFeatureOverlay(
              featureId: 'toggleLocationSnapping',
              tapTarget: Icon(Icons.control_camera),
              title: Text('Location Snapping'),
              description: Text(
                  'This button toggles the map mode between snapping (default) and free. When the icon shown is a circle with a line across it, tap it to switch to free mode, and you'll be able to move, zoom and rotate the map freely. In snapping mode, you will be snapped to your current location and orientation, and you'll also be unable to pan, rotate or zoom (usually).'),
              //overflowMode: OverflowMode.extendBackground,
              backgroundColor: Theme.of(context).primaryColor,
              targetColor: Colors.white,
              textColor: Colors.white,
              child: CircularButton(
                icon: Icon(moveMapToUser
                    ? Icons.location_disabled
                    : Icons.my_location),
                onPressed: () {
                  setState(() {
                    moveMapToUser = !moveMapToUser;
                    rotateMapToUser = false;
                  });
                },
              ),
            ),
          ),
          FloatingSearchBarAction(
            showIfOpened: false,
            child: Visibility(
              visible: moveMapToUser,
              child: DescribedFeatureOverlay(
                featureId: 'rotationAndNorth',
                tapTarget: Icon(Icons.screen_rotation),
                title: Text('Rotation & Panning Mode'),
                description: Text(
                    'This button has three states. When showing an upward facing arrow in free mode, tap it to orientate the map north, and remain in free mode. When showing a lock symbol in snapping mode, tap it to renable automatic rotation and prevent zooming. When showing an unlock symbol in snapping mode, tap it to allow rotation and zooming, but still prevent panning away from your current location.'),
                //overflowMode: OverflowMode.extendBackground,
                backgroundColor: Theme.of(context).primaryColor,
                targetColor: Colors.white,
                textColor: Colors.white,
                child: CircularButton(
                  icon: Icon(rotateMapToUser ? Icons.lock_open : Icons.lock),
                  onPressed: () {
                    setState(() {
                      rotateMapToUser = !rotateMapToUser;
                      if (!rotateMapToUser) {
                        controller.rotate(0);
                      }
                    });
                  },
                ),
              ),
            ),
          ),
          FloatingSearchBarAction(
            showIfOpened: false,
            child: Visibility(
              visible: !moveMapToUser,
              child: CircularButton(
                icon: Icon(Icons.north),
                onPressed: () {
                  setState(() {
                    controller.rotate(0);
                  });
                },
              ),
            ),
          ),
          FloatingSearchBarAction(
            showIfOpened: false,
            child: Visibility(
              visible: !offlineMode,
              child: DescribedFeatureOverlay(
                featureId: 'refresh',
                tapTarget: Icon(Icons.refresh),
                title: Text('Refresh'),
                description: Text(
                    'Tap this button to search the area visible on your device for GeoTags after panning the map.'),
                //overflowMode: OverflowMode.extendBackground,
                backgroundColor: Theme.of(context).primaryColor,
                targetColor: Colors.white,
                textColor: Colors.white,
                child: CircularButton(
                    icon: const Icon(Icons.refresh),
                    onPressed: () {
                      //setState(() {
                      paintGeoTags(
                          true,
                          () => setState(() {
                                loadingSearch = true;
                              }),
                          () => setState(() {
                                loadingSearch = false;
                              }));
                      //});
                    }),
              ),
            ),
          ),
          FloatingSearchBarAction( //Profile Pic icon
            showIfOpened: !compassExpanded, //Don't worry about compassExpanded
            child: Visibility(
              visible: !offlineMode, //Should update on setState
              child: DescribedFeatureOverlay(
                featureId: 'profile',
                tapTarget: Icon(Icons.account_circle),
                title: Text('Public Profile'),
                description: Text(
                    'Tap this button to view your public profile, and view information such as rank, total points and various other information. You can manage your profile by tapping the settings cog icon in that screen, or by choosing Manage My Profile from the menu. The colored border represents the color of your current rank, and the point will always face north. You can hold down on this icon to toggle the visibility of the compass point and to expand or reduce the height of the search bar. 

After you've tapped the icon above to move to the next button introduction, tap the menu button in the top left, and your introduction will continue there!'),
                overflowMode: OverflowMode.extendBackground,
                backgroundColor: Theme.of(context).primaryColor,
                targetColor: Colors.white,
                textColor: Colors.white,
                child: Hero(
                  tag: 'profileImage',
                  child: GestureDetector(
                    onTap: () => Navigator.pushNamed(
                      context,
  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Widget is updating on inner widget. but all state is not being changed. you should pass state to all your inner widgets like this (in showalert in your case)

StatefulBuilder(builder: (context, newState) {
                           return 
                         showAlert(
                          context,
                          widget.mainContext,
                          'Offline Mode',
                          <Widget>[
                            //Some text widgets
                          ],
                          'Agree & Enable Offline Mode',
                          'Cancel', () {
                        StorageManager.saveData('offlineMode', 'true');
                        newState(() {
                          offlineMode = true; //Should Update
                        });
                        if (!Directory(globals.saveDir.path + '/tiles')
                            .existsSync()) {
                          Directory(globals.saveDir.path + '/tiles')
                              .createSync(recursive: true);
                        }
                      });
});

Please try this! I didnt test, but the logic is you should pass the state. Maybe you should wrap listtile with new state..


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...