0

I'm trying to accomplish this:

  1. I have a Stateful Widget PageModule which holds a PageView with a bottom bar
  2. Once I click the central button it opens a bottomsheet to select if I want to pick an image from camera or from gallery
  3. Based on selection I need to open the NewPost widget taking into account the selection for using the image_picker package.

Problem is when I choose the value from bottomsheet. I can't make the NewPost widget to rebuild with the choice selected.

What approach should I follow here in order to open the proper ImageSource everytime I select the source from the bottomsheet?

I hope my request is clear. In my code here below I tried to implement the Provider package hoping to solve the issue, but I'm surely missing the point here.

PageModule

class PageModule extends StatefulWidget {
  static const String id = 'start';
  @override
  _PageModuleState createState() => _PageModuleState();
}

class _PageModuleState extends State<PageModule> {
  int _selectedIndex = 0;
  String source = 'gallery';

  PageController pageController = PageController(
    initialPage: 0,
    keepPage: true,
  );

  void onPageChange(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  void onTabChange(int index) {
    setState(() {
      _selectedIndex = index;
      pageController.jumpToPage(index);
    });
  }

  void _bottomSheet() {
    showModalBottomSheet(
      shape: kBottomSheetShape,
      context: context,
      builder: (context) => CameraSelector(
        onAction: (String value) {
          Provider.of<SharedData>(context).changeSource(value);
          onTabChange(2);
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Provider<SharedData>(
      create: (context) => SharedData(),
      child: Scaffold(
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.camera),
          onPressed: () {
            _bottomSheet();
          },
        ),
        bottomNavigationBar: BottomAppBar(
          shape: CircularNotchedRectangle(),
          notchMargin: 4.0,
          child: new Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.home),
                onPressed: () {
                  onTabChange(0);
                },
              ),
              IconButton(
                icon: Icon(Icons.search),
                onPressed: () {
                  onTabChange(1);
                },
              ),
              SizedBox(
                width: 50.0,
              ),
              IconButton(
                icon: Icon(Icons.notifications),
                onPressed: () {
                  onTabChange(3);
                },
              ),
              IconButton(
                icon: Icon(Icons.person_pin),
                onPressed: () {
                  onTabChange(4);
                },
              ),
            ],
          ),
        ),
        body: PageView(
          controller: pageController,
          physics: NeverScrollableScrollPhysics(),
          onPageChanged: (index) {
            onPageChange(index);
          },
          children: <Widget>[
            HomeScreen(),
            SearchScreen(),
            NewPostScreen(),
            NotificationScreen(),
            ProfileScreen(),
          ],
        ),
      ),
    );
  }
}

CameraSelector bottomsheet

class CameraSelector extends StatelessWidget {
  final Function onAction;

  CameraSelector({@required this.onAction});

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Container(
          padding: EdgeInsets.only(bottom: 45, top: 25),
          child: Column(
            children: <Widget>[
              Heading(
                size: kH2,
                text: 'Scegli una opzione',
                bold: true,
              ),
              SizedBox(
                height: 20,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  SelectorButton(
                    icon: Icons.photo_size_select_actual,
                    label: 'Libreria',
                    source: Source.gallery,
                    action: (String value) {
                      onAction(value.toLowerCase());
                    },
                  ),
                  SizedBox(
                    width: 20,
                  ),
                  SelectorButton(
                    icon: Icons.camera_alt,
                    label: 'Camera',
                    source: Source.camera,
                    action: (String value) {
                      onAction(value.toLowerCase());
                    },
                  ),
                ],
              ),
            ],
          )),
    );
  }
}

class SelectorButton extends StatelessWidget {
  final IconData icon;
  final String label;
  final String source;
  final Function action;

  SelectorButton({
    @required this.icon,
    @required this.label,
    @required this.source,
    @required this.action,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(20)),
      ),
      child: InkWell(
        onTap: () {
          Navigator.pop(context);
          action(source);
        },
        borderRadius: BorderRadius.all(Radius.circular(20)),
        child: Container(
          padding: EdgeInsets.symmetric(
            vertical: 20,
            horizontal: 30,
          ),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(20)),
            border: Border.all(color: Colors.grey[850], width: 1.5),
          ),
          child: Column(
            children: <Widget>[
              Icon(
                icon,
                size: 50.0,
                color: Colors.grey[850],
              ),
              SizedBox(
                height: 5,
              ),
              Text(
                label.toUpperCase(),
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 15,
                  color: Colors.grey[850],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

NewPost

  static const String id = 'newpost';
  @override
  _NewPostScreenState createState() => _NewPostScreenState();
}

class _NewPostScreenState extends State<NewPostScreen> {
  File _image;

  Future getImage() async {
    var image = await ImagePicker.pickImage(
      source: Provider.of<SharedData>(context).source == 'gallery' ? ImageSource.gallery : ImageSource.camera,
      imageQuality: 100,
      maxWidth: 1200,
    );

    setState(() {
      _image = image;
    });
  }

  @override
  void initState() {
    super.initState();
    getImage();
  }

  Widget _bodyBuild() {
    if (_image != null) {
      return ListView(
        shrinkWrap: true,
        padding: EdgeInsets.all(20.0),
        children: <Widget>[
          Heading(
            size: kH1,
            text: 'Dettagli del post',
            bold: true,
          ),
          SizedBox(
            height: 20.0,
          ),
          Container(
            height: 100.0,
            child: Center(
              child: Row(
                children: <Widget>[
                  Container(
                    width: 100,
                    child: _image == null
                        ? Text('')
                        : ProfileImage(
                            size: 50,
                            image: _image,
                          ),
                  ),
                  SizedBox(
                    width: 20.0,
                  ),
                  Text('qui box per descrizione')
                ],
              ),
            ),
          ),
          SizedBox(
            height: 20.0,
          ),
          Container(
            decoration: BoxDecoration(
              border: Border(top: BorderSide(width: 1.0, color: Colors.grey[400])),
            ),
          ),
          SizedBox(
            height: 20.0,
          ),
          Heading(
            size: kH3,
            text: 'Tagga gli amici',
            bold: true,
          ),
          SizedBox(
            height: 20.0,
          ),
          Text('lista dei tag degli amici con un input di ricerca con autocompletamento dei risultati'),
          SizedBox(
            height: 20.0,
          ),
          Container(
            decoration: BoxDecoration(
              border: Border(top: BorderSide(width: 1.0, color: Colors.grey[400])),
            ),
          ),
          SizedBox(
            height: 20.0,
          ),
          Heading(
            size: kH3,
            text: 'Aggiungi tag',
            bold: true,
          ),
          SizedBox(
            height: 20.0,
          ),
          Text('lista dei tag argomenti con un input di ricerca con autocompletamento dei risultati')
        ],
      );
    } else {
      return Container();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Nuovo Post'),
      ),
      body: _bodyBuild(),
    );
  }
}
Nemesis19
  • 13
  • 5

1 Answers1

1

Since you are using provider, instead of initState, what you are looking for is didChangeDependencies/build

The idea is that instead of initializing your state once on creation, you are initializing it when the state of a provider changes

Here's how it would look like:

class _MyState extends State<MyStatefulWidget> {
  SomeClass previousSomeClass;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final someClass = Provider.of<Something>(context).someClass;
    if (someClass != previousSomeClass) {
      previousSomeClass = someClass;
      getImage();
    }
  }

  void getImage() {...}
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Thanks it's a pleasure to get an answer from the package creator. Now I have a different problem. After implementing your code, once I make a selection in bottomsheet, the console output this error `Tried to listen to a value exposed with provider, from outside of the widget tree. This is likely caused by an event handler (like a button's onPressed) that called Provider.of without passing `listen: false`. To fix, write: Provider.of(context, listen: false); ` I added the listen: false to the Provider.of in _bottomsheet function, but leads to another error. – Nemesis19 Mar 11 '20 at 14:42
  • I've also tried to implement your suggestion here: https://stackoverflow.com/questions/57547784/how-to-access-provided-provider-of-value-inside-showmodalbottomsheet ...but no luck – Nemesis19 Mar 11 '20 at 15:52